CORS
跨源资源共享(CORS,Cross-Origin Resource Sharing)定义了浏览器与服务器如何实现跨源通信 CORS背后的思路就是通过使用自定义HTTP头部来实现。
在进行跨域请求时,浏览器会把请求分为两种,简答请求和非简单请求,两种请求的处理方式不一样。
简单请求
简单请求是为了兼容表单,因为表单是可以跨域进行的。 满足以下条件即为简单请求。
- 请求方法为
GET
、POST
、HEAD
方法 - 请求头为
Accept
、Accept-Language
、Content-Type
content-type
只能为application/x-www-formurlencoded
、multipart/form-data
、text/plain
,除了这三种,例如application/json
即为非简单请求。
浏览器在发出简单请求时,会在HTTP头加一个Origin
字段,表明请求的源(协议+域名+端口)。
服务器根据源判断该源是否在许可范围内。
如果该源不在许可范围内,则服务器正常响应。但浏览器在响应中找不到Access-Control-Allow-Origin
这个响应头,则会抛出错误,被xhr
的error
事件处理程序捕捉。
如果该源在许可范围内,则会在响应头部加入Access-Control-Allow-Origin
为请求源或 *,
其它响应头:
Access-Control-Allow-Headers
,表示可以在请求头出现的字段。Access-Control-Allow-Methods
,表示可以请求的方法Access-Control-Allow-Credentials
,表示是否可以携带Cookie
如果一个请求需要携带Cookie,假如是通过XMLHTTPRequest对象来发出
那么客户端要设置xhr.withCredentials为true,这个一般默认为true,可以设置为false关闭,不发送Cookie
服务端要设置Access-Control-Allow-Credentials为true
非简单请求
除了简单请求,那就是非简单请求了。 非简单请求之前,浏览器会先发送一个预检请求,根据响应决定是否发送请求。
预检请求
预检请求是一个option
请求,主要增加3个头信息
Origin
表明源Access-Control-Request-Method
,表示请求的方法Access-Control-Request-Headers
,表示请求要用到的头部
服务器看看请求的方法,其它请求头是否在自己的许可范围内。如果是,那么就返回相对应的字段。
如果服务器不支持该跨域请求,就返回正常的HTTP响应,即不带Access-Control-Allow-**
类似的字段,浏览器看到了会自动拦截报错的。
Access-Control-Allow-Max-Age
貌似是缓存预检请求响应,单位为秒,但是我试了好像并没有什么效果。
如果预检请求通过后,就正常的发起请求,多加一个Origin
字段。
如果预检请求没通过,就没有后续了,不会再发东西。
响应需要一直携带Access-Control-Allow-xx
响应头,否则通过了预检请求也不好使。后面的请求也会被浏览器所拦截
JSONP
JSONP是利用<script>
标签不受跨域的影响,从而可以获取资源,仅限GET
.
前端代码
function JSONP(src, callback, id) {
let script = document.createElement("script");
script.src = src + "?callback=" + callback + "&id=" + id;
document.body.append(script);
}
function getPerson(res) {
console.log(res);
}
JSONP("http://localhost:3000", "getPerson", 1);
服务端代码
let person = [
{ id: 1, name: "zs", age: 22 },
{ id: 2, name: "ls", age: 20 },
];
server.get("/", (req, res) => {
let cb = req.query.callback
let id = req.query.id || 0
let someone = person.find(ele => ele.id == id) || "查无此人"
res.send(`${cb}(${JSON.stringify(someone)})`);
});
服务端返回一个字符串,前端请求回来当作JS代码执行,执行事先定义好的方法,服务端把前端需要的值放在方法的参数中
Content-Type
application/x-www-formurlencoded
和multipart/form-data
都是表单发送编码格式,有啥区别
application/x-www-formurlencoded
把键值对组成name=zs&age=22
这种表单格式发送。
multipart/form-data
则是通过boundary
分割