CORS
什么是CORS:
CORS(Cross-origin resource sharing),跨域资源共享,是⼀份浏览器技术的规范,⽤来避开 浏览器的同源策略 简单来说就是解决跨域问题的除了jsonp外的另⼀种⽅法;⽐jsonp更加优雅。
我们现在来准备两个服务器和一个前端页面
前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>3000端口</h1>
<button class="btn1">请求非跨域/同源</button>
<button class="btn2">请求跨域/非同源</button>
<script>
document.querySelector(".btn1").onclick = function(){
let xhr = new XMLHttpRequest();
xhr.open("post","/post",true);
xhr.onload = function(){
console.log(xhr.responseText);
};
xhr.send();
};
document.querySelector(".btn2").onclick = function(){
let xhr = new XMLHttpRequest();
xhr.open("post","http://localhost:4000/post",true);// 路径不能写成相对路径
xhr.onload = function(){
console.log(xhr.responseText);
};
xhr.send();
}
</script>
</body>
</html>
两个服务器文件
const Koa = require("koa");
const static = require("koa-static");
const Router = require("koa-router");
let app = new Koa();
let router = new Router();
app.use(static(__dirname+"/static"));
router.get("/",ctx=>{
ctx.body = "3000端口"
});
router.post("/post",ctx=>{
ctx.body = "3000端口--同源"
})
app.use(router.routes());
app.listen(3000)
const Koa = require("koa");
const static = require("koa-static");
const Router = require("koa-router");
const koaBody = require("koa-body")
let app = new Koa();
let router = new Router();
app.use(static(__dirname + "/static"));
app.use(koaBody());
router.get("/index", ctx => {
ctx.body = "4000端口"
});
router.post("/post",ctx=>{
ctx.body = "4000端口--非同源"
})
app.use(router.routes());
app.listen(4000)
注意点: 两个服务器文件都要打开
浏览器反馈
结论 这种情况呢跨域是请求成功了,但是我们为什么拿不到数据?因为跨域是一种浏览器行为,服务器端没有跨域、同源这种说法,浏览器基于同源策略所以没有响应,所以除了jsonp这种方法外就是我们今天要了解的CORS
CORS的处理也非常简单
我们只需要在4000端口的服务器文件里设置一下头部信息就可以了
router.post("/post",ctx=>{
// 允许跨域
ctx.set("Access-Control-Allow-Origin","*");
ctx.body = "4000端口--非同源"
})
浏览器反馈
注意点!!!
- 不安全——如果设置成这样就相当于把浏览器的同源策略毁掉,全世界都可以找到你并获取到你的数据
- 不能携带凭证(待会会说到凭证是什么)
综上所述 我们一般不这样设置,我们需要给他设置成指定的
router.post("/post",ctx=>{
// 1. 允许跨域
// ctx.set("Access-Control-Allow-Origin","*");
ctx.set("Access-Control-Allow-Origin","http://loaclhost:3000");
ctx.body = "4000端口--非同源"
});
现在我们从新回到页面来说,来获取返还头部信息
document.querySelector(".btn2").onclick = function(){
let xhr = new XMLHttpRequest();
xhr.open("post","http://localhost:4000/post",true);// 路径不能写成相对路径
xhr.onload = function(){
console.log(xhr.responseText);
// 获取返还头部信息
let res = xhr.getAllResponseHeaders();
console.log(res);
};
xhr.send();
}
浏览器反馈
但是头部信息还是挺多的,没有获取完整
所以这个方法不能获取到所有的头部信息 但是这不是方法的问题,这是服务器端在你想要获取到这些信息做功能是,服务器不让你获取到,所以我们还是在设置一下,允许获取头部信息
router.post("/post",ctx=>{
// 1. 允许跨域
// ctx.set("Access-Control-Allow-Origin","*");
ctx.set("Access-Control-Allow-Origin","http://localhost:3000");
// 2. 允许获取头部信息
ctx.set("Access-Control-Expose-Headers", "Content-Type,Content-Length,Date");
ctx.body = "4000端口--非同源"
});
浏览器反馈
CORS的其他设置指令
router.post("/post", ctx => {
// 1. 允许跨域
ctx.set("Access-Control-Allow-Origin", "http://localhost:3000");
// 2.允许获取的头部信息(响应)
ctx.set("Access-Control-Expose-Headers", "Content-Type,Content-Length,Date");
// 3.设置允许前端发送的请求方式
ctx.set("Access-Control-Allow-Methods","GET,DELETE,HEAD,OPTIONS");
// 4.允许前端设置的头部(请求)
ctx.set("Access-Control-Allow-Headers","Content-Type,Content-Length,text");
ctx.body = "4000端口--非同源";
});
允许携带凭证 后端的服务器需要设置一下,前端的页面也需要开启一下
xhr.withCredentials = true; //写在前端页面
ctx.set("Access-Control-Allow-Credentials",true); //写在后端页面
预检请求
开启默认
document.querySelector(".btn2").onclick = function(){
/*
跨域:请求成功了
跨域是浏览器行为,服务器端没有跨域、同源这种说法
*/
let xhr = new XMLHttpRequest();
xhr.open("post","http://localhost:4000/post",true);// 路径不能写成相对路径
// 设置请求头部
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
// 允许跨域请求携带凭证
xhr.withCredentials = true;
xhr.onload = function(){
console.log(xhr.responseText);
// 获取返还头部信息
let res = xhr.getAllResponseHeaders();
console.log(res);
};
xhr.send();
}
浏览器反馈
修改请求头
document.querySelector(".btn2").onclick = function(){
/*
跨域:请求成功了
跨域是浏览器行为,服务器端没有跨域、同源这种说法
*/
let xhr = new XMLHttpRequest();
xhr.open("post","http://localhost:4000/post",true);// 路径不能写成相对路径
// 设置请求头部
xhr.setRequestHeader("Content-Type","application/json");
// 允许跨域请求携带凭证
xhr.withCredentials = true;
xhr.onload = function(){
console.log(xhr.responseText);
// 获取返还头部信息
let res = xhr.getAllResponseHeaders();
console.log(res);
};
xhr.send();
}
浏览器反馈
这种情况属于预检请求
什么是预检请求 是一种请求方式,不再是get与post了,而是options
- 你要去处理一下options 它和get与post一样,也会发起一个请求
- 你要去允许前端设置头部信息
处理预检请求
router.options("/*",ctx=>{
// 1. 允许跨域
ctx.set("Access-Control-Allow-Origin", "http://localhost:3000");
// 2. 允许获取的头部信息(响应)
ctx.set("Access-Control-Expose-Headers", "Content-Type,Content-Length,Date");
// 3. 设置允许前端发送的请求方式
ctx.set("Access-Control-Allow-Methods","GET,DELETE,HEAD,OPTIONS");
// 4. 允许前端设置的头部(请求)
ctx.set("Access-Control-Allow-Headers","Content-Type,Content-Length,text");
// 5. 允许携带凭证
ctx.set("Access-Control-Allow-Credentials",true);
console.log("有预检请求");
ctx.body = "";
})
什么时候会出现预检请求
- 指定的请求类型是(PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH)
- 修改了指定的属性
- 修改指定的属性值
整体思路导图
注意 CORS内除了手动设置允许跨域是必须要写的,其他的都是看自己需求。
必须是下面定义对CORS安全的首部字段集合,不能是集合之外的其他首部字段。
Accept、Accept-Language、Content-Language、Content-Type、DPR、Downlink、Save-Data、Viewport-Width、Width。
Content-Type的值必须是text/plain、multipart/form-data、application/x-www-form-urlencoded中任意一个值
满足上面所有的条件才不会发送预检请求,在实际项目中我们的请求格式可能是application/json格式编码,或者使用自定义请求头都会触发CORS的预检请求。
所以,在项目中是否会触发CORS的预检请求要做到心中有数。