原文地址 Using CORSjavascript
部份内容我精简了部份内容, 若是想看所有能够去上面的原文地址查看html
同时也附上脑图前端
我如今也弄不清楚这篇文章算不算是翻译html5
Cross-Origin Resource Sharing(CORS) 是W3C为浏览器制定的能够跨域通讯的规范. 经过使用 XMLHttpRequest 对象, CORS可让开发者方便的进行跨域通讯, 就像在使用同域通讯同样.java
CORS的使用十分简单. 想象一下有一个网站 a.com 想要获取另外一个网站 b.com 的数据. 但因为浏览器的同源策略, 这样的请求将会被禁止. 这时咱们可使用CORS, 经过添加一些特殊的请求响应头, 可让 a.com 访问 b.com 的数据.git
经过上面的例子咱们能够看出, CORS的支持须要客户端和服务器同时支持才行. 幸运的是, 若是你是一名客户端的开发人员(如前端工程师), 绝大多数的技术细节都会被隐藏掉.github
这篇文章将讲述客户端如何发送一个跨域请求, 而服务器又将如何去处理和支持跨域请求.跨域
时至今日, 发送一个XMLHttprequest请求已是一个简单的事情, 这里我不在过多赘述.浏览器
根据浏览器的同源策略, 当请求的地址与来源地址的协议域名端口中的任一值不相同时, 均视为是一个跨域的请求.缓存
跨域请求一般不会携带cookies信息. 为了能让跨域请求带上cookies, 你须要将作以下设置:
xhr.withCredentials = true;
为了能让这个属性正常工做, 你还须要在服务器端在响应是带上Access-Control-Allow-Credentials
, 同时它的值必须为true. 更多的内容能够看服务器设置的那一部分.
Access-Control-Allow-Credentials: true
设置withCredentials为true后, 在于服务器进行通讯时会携带这个域名下的全部cookies, 同时服务器也能够在它的于域名下设置cookies. 但值得注意的是, 这些cookies仍然遵照浏览器的同源策略, 你没法经过javascript访问这个域名下的cookies, 它只被这个域名的服务器控制.
大部分跨域请求的重要操做实在浏览器和服务器之间进行的. 浏览器在跨域请求期间会表明客户端在请求上增长行的请求头, 有的时候还会增长新的请求. 这些操做对于开发者来讲是透明, 可是这些请求仍然能够被抓包工具捕获.
浏览器的开发者来实现浏览器端的跨域请求细节, 这节内容来介绍如何配置服务器来支持跨域请求.
一般将跨越请求分为"简单请求"和"非简单请求"两类.
简单请求遵循如下的规则
首先它的请求方式只能是GET, POST, PUT
同时的它的请求头部只能包含上面的那几个类型, 值的注意的是Content-Type
只能是罗列出的三种(正好是form的entryType的三个值).
对于简单请求, 浏览器能够自行解决其中的跨域问题. 例如咱们熟知的一个跨域通讯的解决方式JSON-P
就是利用GET发送一个简单请求来规避跨域的问题.HTML中的表单提交也不须要处理跨域问题.
任何不符合上述条件的请求都算做非简单请求, 浏览器在处理非简单的跨域请求时会与服务器进行额外的通讯(称之为预检请求), 将在下面介绍.
经过这个cors-demo咱们能够方便的查看浏览器与服务器之间的同信.
当浏览器发送一个简单请求时, 咱们打开浏览器的Network面板能够看到一个以下的请求(删除部份内容)
GET /get HTTP/1.1 Host: localhost:5051 Origin: http://localhost:8080 User-Agent: Mozilla/5.0 .... Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,en;q=0.8,zh;q=0.6,ja;q=0.4,zh-TW;q=0.2
咱们须要注意的一点是, 全部的跨域请求(不管简单或者非简单)总会包含一个Origin
的请求头部, 这个属性的值由浏览器添加, 并且不受用户控制. 它的值由协议(如: http), 域名(如: a.om)和端口(只有它不是默认值时才包含, 如80端口)组成, 说明请求的来源.
包含Origin
的请求不必定是跨域请求, 可是跨域请求必定包含Origin
. 一些同源的请求一样也会包含Origin
请求头.例如, Firefox浏览器不会在同源的请求中添加Origin
, 可是Chrome和Safari会在同源的POST/PUT/DELETE请求中添加Origin
请求头(可是同源的GET不会添加).
浏览器会忽略掉同源请求中的的CORS响应中的设置.
而后咱们来看一个有效的跨域请求响应
Access-Control-Allow-Origin: http://localhost:8080 Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8
全部与跨域请求相关的HTTP头部都以Access-Control-
开始, 下面是它们的详细信息
Access-Control-Allow-Origin (必选)
全部有效的跨域响应都必须包含这个请求头, 没有的话会致使跨域请求失败. 它的值能够是请求中的Origin
的值, 也能够设置为*
来表示能够响应全部来源的请求.
Access-Control-Allow-Credentials (可选)
默认状况下跨域请求不会携带cookies信息. 若是须要请求携带cookies信息, 则须要将这个值设置为true
, 若是不须要就不要设置这个值, 而不是将它设置为false
.
这个请求头须要与 withCredentials 配合使用. 只有两个值都设置为true
的时候才可以在请求中携带cookies信息. 当withCredentials
设置为true
, 而响应中不包含Access-Control-Allow-Credentials
时, 请求会发生错误.
Access-Control-Expose-Headers (可选)
XMLHttpRequest2对象上的getResponseHeader()
方法可让你获取到响应中头部信息, 但在跨域请求中,你只能获取到如下信息
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
若是你但愿客户端能过获取其余的头部信息, 能够设置这个值.
对与开发者来讲, 发送一个跨域的非简单请求跟发送一个同域请求没什么区别.但事实上浏览器会发送两个请求, 第一个请求(成为预检请求)会像服务器肯定是否接受这个跨域请求, 第二个才是真正的发出请求. 浏览器自动的处理这两个请求, 同时预检请求也是能够被缓存的, 而不用每次请求都须要发送预检请求.
下面是一个预检请求
OPTIONS /cors/post HTTP/1.1 Host: localhost:5051 Access-Control-Request-Method: POST Access-Control-Request-Headers: content-type Origin: http://localhost:8080 User-Agent: Mozilla/5.0 ... DNT: 1 Referer: http://localhost:8080/ Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,en;q=0.8,zh;q=0.6,ja;q=0.4,zh-TW;q=0.2
如同简单跨域请求同样, 在预检请求中也包含了Origin
请求头, 同时这个请求的方式OPTIONS
(因此你必须肯定你的服务器可以正常的处理这中请求). 它同时也包含了其余的请求头.
Access-Control-Request-Method
这个请求头的值就是正式请求的请求方式, 上面的那个例子就是POST
Access-Control-Request-Headers
它的值是一个由逗号分隔的正式请求中请求头的列表.
预检请求是在实际的请求发出前先向服务器确认是否可以处理这个请求. 服务器应该检查上边两个请求头的值, 来判断这个请求是否有效.
若是服务器确认这个请求有效, 那么它会作出以下的响应
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://localhost:8080 Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Methods: GET, POST, PUT, DELETE Content-Type: text/html Date: Sat, 12 Aug 2017 13:46:08 GMT Connection: keep-alive Transfer-Encoding: chunked
Access-Control-Allow-Origin (必选)
如同简单跨域请求同样, 预检请求的响应也必须包含这个值
Access-Control-Allow-Methods (必选)
由逗号分隔的HTTP请求方式, 在其中的值表示服务可以接受这中请求方式的跨域请求.
值得注意的是虽然预检请求只是针对单个请求方式进行检测, 可是你仍能够返回一个你所支持的请求方式列表.这样作的好处是方便对预检请求进行缓存.
Access-Control-Allow-Headers (当预检请求中包含Access-Control-Request-Headers时是必须的)
由逗号分隔的支持的请求头部列表, 与Access-Control-Allow-Methods
相似, 虽然预检请求中只有不多的一部分请求头, 可是你仍然能够返回全部你支持的列表, 缘由也是为了缓存.
Access-Control-Allow-Credentials (可选)
同简单请求
Access-Control-Max-Age (可选)
在每一个请求前面都发送一个预检请求是很浪费资源的, 这个值容许你设置预检请求的缓存时间, 单位是秒.
一旦预检请求经过服务器的检查, 那么浏览器会随后发送实际的请求. 实际请求的处理与简单请求同样.
若是服务器想要拒绝一个跨域请求, 那么他能够直接回复一个简单的响应(如 HTTP 200), 但在响应头中不要包含任何与CORS相关的响应头设置. 服务器也可能会由于预检请求不合法而拒绝这个请求. 若是预检请求中不包含正确的CORS头部设置, 它就不会发送实际的请求.
当跨域请求发生错时, 浏览器会调用onerror
事件, 同时会在控制台打印相关的错误信息.
最后附上一个服务器端处理跨域请求的流程图