跨域资源共享(Cross-Origin Resource Sharing)是一种机制,它使用额外的 HTTP 头部告诉浏览器可让一个web应用进行跨域资源请求。前端
若一个请求同时知足下述全部条件,则该请求可视为“简单请求”<span style="color:grey">(注:灰色字体内容了解便可)</span>:ios
使用的方法为git
GET
HEAD
POST
手动设置的头部字段只能是(注意:也能够设置 Forbidden header name 中的头部字段,如 Connection
、Accept-Encoding
等,可是设置无效)github
Accept
Accept-Language
Content-Language
Content-Type
(值的范围还要符合下面的要求)DPR
</div>Downlink
</div>Save-Data
</div>Viewpoer-Width
</div>Width
</div>Content-Type
的值只能为web
application/x-www-form-urlencoded
multipart/form-data
text/plain
XMLHttpRequestUpload
object used in the request; these are accessed using the XMLHttpRequest.upload property.</div>CORS 预检请求发生在实际请求以前,用于检查服务器是否支持 CORS,以判断实际请求发送是否安全。预检请求使用的方式是 OPTIONS
。axios
当一个请求不是“简单请求”时,即应该先发送预检请求,好比:后端
GET
、HEAD
、POST
X-xxx
Content-Type
值不是 application/x-www-form-urlencoded
、 multipart/form-data
、text/plain
,等等跨域请求,CORS要求服务端设置一些头部字段,最重要的一个就是 Access-Control-Allow-Origin
。下面以案例进行说明,前端使用 axios 进行 http 传输,后端以 koa 做为服务端框架,并使用CORS中间件 koa2-cors。api
// Client http://localhost:8080 simpleRequest() { axios({ method: 'GET', url: 'http://localhost:3000/api/simple' }).then(data => { console.log(data); }); }
// Server http://localhost:3000 app.use(cors()); router.get('/api/simple', ctx => { ctx.body = { result: 'simple request success' }; });
HTTP 报文:跨域
HTTP 请求头部有个 Origin
字段,表示请求来自哪里。HTTP 响应头部中的 Access-Control-Allow-Origin
表示哪一个域能够访问该资源。使用 Origin
和 Access-Control-Allow-Origin
就完成了最简单的访问控制。浏览器
// Client http://localhost:8080 mainRequest() { axios({ method: 'POST', url: 'http://localhost:3000/api/mainRequest', headers: { 'X-test': 'CORS' } // 增长一个自定义的头部字段,触发预检请求 }).then(data => { console.log(data); }); }
// Server http://localhost:3000 app.use(cors()); router.post('/api/mainRequest', ctx => { ctx.body = { result: 'main request success' }; });
预检请求的报文:
请求首部字段 Access-Control-Request-Method
告知服务器,实际请求将使用 POST
方法。
请求首部字段 Access-Control-Request-Headers
告知服务器,实际请求将携带一个自定义请求首部字段:x-test。服务器据此决定,该实际请求是否被容许。
响应首部字段 Access-Control-Allow-Methods
代表服务器容许客户端使用哪些方法发起请求。
响应首部字段 Access-Control-Allow-Headers
代表服务器容许请求中携带字段 x-test。
实际请求的报文:
实际请求中发送了 X-test
头部字段,响应状态码 200 OK。
能够看到,预检请求中 Client 和 Server 使用了更多的头部字段来完成访问控制。那么,CORS 相关的请求头部字段和响应头部字段共有哪些呢?
Origin
头部字段表示预检请求或实际请求的源站。Access-Control-Request-Method
头部字段用于预检请求。其做用是,将实际请求所使用的 HTTP 方法告诉服务器。Access-Control-Request-Headers
头部字段用于预检请求。其做用是,将实际请求所携带的首部字段告诉服务器。注意,以上请求头部字段无须手动设置,当使用 XMLHttpRequest
对象发起跨域请求时,它们已经被设置就绪。
Access-Control-Allow-Origin
其语法以下:
Access-Control-Allow-Origin: <origin> | *
origin 参数的值指定了容许访问该资源的外域 URI。若是该字段的值为通配符 *
,则表示容许来自全部域的请求。
注意,若是服务端指定了具体的域名而非 *
,那么响应头部中的 Vary
字段的值必须包含 Origin
。这将告诉客户端:服务器对不一样的源站返回不一样的内容。
Access-Control-Allow-Methods
头部字段用于预检请求的响应。其指明了实际请求所容许使用的 HTTP 方法。Access-Control-Allow-Headers
头部字段用于预检请求的响应。其指明了实际请求中容许携带的首部字段。Access-Control-Expose-Headers
跨域请求中,浏览器默认状况下经过API只能获取到如下响应头部字段:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
若是想要访问其余响应头部信息,则须要在服务器端设置 Access-Control-Allow-Headers
。Access-Control-Expose-Headers
让服务器把容许浏览器访问的头部字段放入白名单,好比:
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
这样浏览器就可以访问到 X-My-Custom-Header
和 X-Another-Custom-Header
响应头部了。
Access-Control-Max-Age Access-Control-Max-Age
字段指定了预检请求的结果可以被缓存多久,单位是 秒,好比:
Access-Control-Max-Age: 5
表示在第一次预检请求发出后,5s 内再访问该接口时会直接发送实际请求,而不须要先发预检请求。过了 5s 后,会再要求先发送预检请求,以此类推。
app.use( cors({ maxAge: 5 }) );
服务端设置了 5s 缓存,实际请求以下:
注意,若是设置缓存后,发现每次仍是会发送 OPTIONS 请求,请检查你是否是勾选了“禁止缓存”。
Access-Control-Allow-Credentials XMLHttpRequest.withCredentials
(或者 Request.credentials
)表示跨域请求中,user agent 是否应该发送 cookies、authorization headers 或者 TLS client certificates 等凭据。Access-Control-Allow-Credentials
的做用就是:当 credentials 为 “真” 时(XHR和Fetch设置方式不同),Access-Control-Allow-Credentials
告诉浏览器是否把响应内容暴露给前端 JS 代码。好比:
// Client http://localhost:8080 simpleRequest() { axios({ method: 'GET', url: 'http://localhost:3000/api/simple', withCredentials: true // 增长了withCredentials 选项 }).then(data => { console.log(data); }); } // Server http://localhost:3000 app.use( cors({ maxAge: 5, // credentials: true }) );
此时,服务端未设置 credentials: true
,发起请求能看到客户端报错:
若是服务端设置了 credentials: true
则客户端就不会报错了。
预检请求的时候,Access-Control-Allow-Credentials
响应头部字段表示实际请求中是否能够使用 credentials。
关于 CORS 响应头部字段的运用,建议看一下 koa2-cors 中间件的源码。代码只有几十行,特别清晰易懂。
CORS 相关内容如上,了解以后能更好地帮助咱们解决平常联调中出现的问题,好比:出现跨域了服务端怎么设置,axios.post
方法发送一个对象时为何会出现 OPTIONS 请求,代理服务器怎么才能转发cookies等等。