前面的话
前端日问,巩固基础,不打烊!!!
解答
之前小柒也总结过九种跨域方法(里面有相关实例),这篇文章参考阮一峰老师的文章详细梳理一下cors。
简述
cors - 跨域资源共享,需要浏览器和服务器同时支持, 这种通信方式原理其实就是通过自定义http的头部来进行通信。
两种请求
- 简单请求:比如
POST
、GET
、HEAD
- 非简单请求:比如
PUT
、DELET
或者Content-Type
字段类型为application/json
简单请求
如果是简单请求的,浏览器直接发出CORS请求,会自动带上Origin
字段(指定请求来自哪个域名)。
当服务端收到响应后:
-
如果
Origin
指定的源不在服务器允许的范围内,那么服务端会返回一个正常的HTTP
回应。但是这个响应是不带Access-Control-Allow-Origin
字段的,浏览器发现响应头没有包含这个字段,就知道出错了,就会抛出一个错误。(这个错误是由XMLHTTPRequest的onerror回调函数捕获的)。 -
如果
Origin
指定的源在服务器允许的范围内,那么服务端返回的响应头就会带上。Access-Control-Allow-Origin
字段,指定为Origin
字段的值,或者是*
表示接受任意域名的请求。(这个字段是必须的)还可以携带其他的字段:比如
Access-Control-Allow-Credentials
(允许携带cookie) 、Access-Control-Expose-Headers
(允许XMLHTTPRequest对象的getResponseHeader()拿到响应头的其他字段的值)。
跨域如何携带cookie
如果想要携带cookie,必须浏览器与服务器同时支持。
-
浏览器必须开启
XMLHTTPRequest
对象的WithCredentials
属性值为true
.xhr.withCredentials = true;// 前端设置是否带cookie
-
服务器响应头要携带
Access-Control-Allow-Credentials
字段的值为true
.// 允许携带cookie res.setHeader('Access-Control-Allow-Credentials', 'true');
注意:如果携带了cookie,那么服务器的Access-Control-Allow-Origin
字段就不能设置为*
,必须指明的域名。
非简单请求
预检请求
如果是非简单请求,那么正式的CORS
通信前,会进行一次预检请求。浏览器会先询问服务器,当前请求的域名是否被服务器所允许,如果服务器允许的话,才会进行正式的通信。
看一段浏览器的脚本文件:
var url = "http://localhost:8084";
var xhr = new XMLHTTPRequest();
xhr.open("PUT",url,true);
// 设置自定义请求头字段:name
xhr.setRequestHeader('name',"xiaoqi")
xhr.send();
浏览器发现这是一个非简单请求,就会自动发出一个预检请求,预检请求的方法是OPTIONS。
针对上面的脚本,对应预检请求头信息:
OPTIONS /cors HTTP/1.1
Origin: http://localhost:8081
Access-Control-Request-Method: PUT
Access-Control-Request-Headers:name
Connection:keep-alive
可以看到,除了Origin
字段外,还包括两个特殊的字段:Access-Control-Request-Method
(该字段必须,用来列出浏览器CORS请求会用到的HTTP请求方法)、Access-Control-Request-Headers
(指定浏览器CORS请求会额外发送的头信息字段)
预检请求回应
服务器相应的头信息设置,服务器设置,哪些域名可以访问,支持的方法,是否可以携带cookie等
// 设置哪个源可以访问我
res.setHeader('Access-Control-Allow-Origin', origin);
// 允许携带哪个头来访问我
res.setHeader('Access-Control-Allow-Headers', 'name');
// 允许哪个方法访问我
res.setHeader('Access-Control-Allow-Methods', 'PUT');
// 允许携带cookie
res.setHeader('Access-Control-Allow-Credentials', 'true');
// 预检的存在时间
res.setHeader('Access-Control-Max-Age',6);
// 允许返回的头
res.setHeader('Access-Control-Expose-Headers', 'name');
当服务器收到预检请求之后,会检查Origin
、Access-Control-Request-Method
、Access-Control-Request-Headers
字段后,如果允许,就会做出回应:
响应头会带上的字段有:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:8081
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Headers: name
Access-Control-Allow-Credentials: true
...
如果预检请求没有被通过,服务器会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会知道服务器不同意这次预检,返回一个错误,被XMLHttpRequest对象的onerror回调函数捕获 。
正常请求
服务器通过预检请求,以后每次浏览器正常CORS请求,都会和简单请求一样,会有一个Origin字段,服务器的回应也会有Access-Control-Allow-Origin头信息字段。