本文根据JavaScript标准参考教程 整理笔记. 代码也来源于此
CORS
是W3C
标准, 全名叫跨域资源共享Cross-origin resource sharing
它允许浏览器向垮源服务器发出XMLHttpRequest
请求.
CORS
需要浏览器和服务器都支持
浏览器将CORS
分为两类, 简单请求和非简单请求.
只要满足下面的两类就属于简单请求:
- 请求方法是
HEAD
,GET
,POST
三种方法之一 HTTP
的头信息不超过下面的字段
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type
:只限于三个值application/x-www-form-urlencoded
、multipart/form-data
、text/plain
不属于简单请求的就是非简单请求
简单请求
- 浏览器对于简单请求, 在请求报文中增加
origin
字段, 然后直接发送给服务器.origin
字段表示请求来自哪个源(协议+域名+端口) - 服务器接收到请求后, 会发送一个
HTTP
响应, 若响应中包含Access-Control-Allow-Origin
字段则表示发送请求的源在允许的范围内
- 若响应不包含
Access-Control-Allow-Origin
信息, 则表示出错了. 我们可以使用XMLHttpRequest.onerror
的回调函数处理. 不能使用http
状态码, 因为无论服务器是否允许源通信, 服务器都会发送响应给浏览器, 说明状态码为200 - 服务器返回的响应除了
Access-Control-Allow-Origin
属性, 还会有其他属性如:
Access-Control-Allow-Origin
:该字段是必须的。它的值要么是请求时origin
字段的值,要么是一个*
,表示接受任意域名的请求Access-Control-Allow-Credentials
: 可选字段, 为true
表示允许发送Cookie. 同时, 发送时, 必须设置XMLHttpRequest.withCredentials为true
才有效. 请求若服务器不允许浏览器发送, 删除该字段即可.Access-Control-Expose-Headers
: 可选字段, 指定发送请求时的其他头字段
- 若响应不包含
非简单请求
非简单请求会在正式通信之前发送一次HTTP
的查询请求, 称为预检请求
服务器收到预检请求后检查请求中的字段再做出响应
若浏览器收到的响应确定允许垮源通信, 则浏览器就会像简单请求一样做出正常的请求.
预检请求
浏览器发送请求询问当前域名是否支持跨域, 能使用哪些HTTP动词及头字段信息等.
预检请求的头信息:
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
OPTIONS
表示这个请求是询问的,origin
表示预检请求来自哪个源Access-Control-Request-Method
: 该字段必须, 列出浏览器的CORS
请求会用到哪些HTTP
方法Access-Control-Request-Headers
: 该字段是一个逗号分隔的字符串,指定浏览器CORS
请求会额外发送的头信息字段,上例是X-Custom-Header
。这个字段通常在XMLHttpRequest.setRequestHeader()
方法中已经设置好了
预检请求的回应
服务器收到预检请求后, 会检查预检请求的字段是否符合服务器的垮源规定.
若允许垮源, 则做出回应, 服务器会发送一个包含Access-Control-Allow-Origin
字段的响应报文给浏览器, 例如:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
Access-Control-Allow-Methods
:该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次“预检”请求Access-Control-Allow-Headers
: 如果浏览器请求包括Access-Control-Request-Headers
字段,则Access-Control-Allow-Headers
字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在“预检”中请求的字段。Access-Control-Max-Age
: 该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000
秒),即允许缓存该条回应1728000
秒(即20
天),在此期间,不用发出另一条预检请求
浏览器的正常请求和回应
服务器通过了预检请求后, 浏览器发送的请求中就有origin
字段, 服务器响应也会有Access-Control-Allow-Origin
字段, 这些是自动添加的. 就和简单请求一样了.
与JSONP比较
JSONP
只支持GET
请求, 以为动态创建的script
标签只支持GET
请求. 支持老式浏览器
DORS
支持所有的HTTP
请求.