首先通读下 MDN 关于 CORS 的定义 https://developer.mozilla.org/zh-CN/docs/web/HTTP/Access_control_CORS , 了解跨域的含义及简单请求和复杂请求等的定义. 文中的内容不赘述, 现在说解决方案.
通过定义我们可以, 简单请求与复杂请求的差别是复杂请求会自动发出一个 OPTIONS 的预检请求, 当请求得到确认后, 才开始真正发送请求.
综上, 我们要解决两个问题:
OPTIONS 请求的正确响应
跨域请求正确响应
Q1: OPTIONS 请求的正确响应
解决的方式有多种, 既可以在 Web Server 解决, 也可以在源码层解决. 因为问题比较普遍, 故我们选择在 Web Server 解决, 下面我们以 Nginx 为例, 说明解决方案.
假设访问的地址为 /example , Nginx 配置如下:
- location /example {
- proxy_redirect off;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_pass http://127.0.0.1:8080/;
- }
为了解决跨域问题, 添加如下内容:
- location /example {+ if ($request_method = 'OPTIONS') {
- + add_header Access-Control-Allow-Origin *;
- + add_header Access-Control-Max-Age 1728000;
- + add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
- + add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
- + add_header Content-Type''text/plain; charset=utf-8';
- + add_header Content-Length 0 ;
- + return 204;
- + }
- proxy_redirect off;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_pass http://127.0.0.1:8080/;
- }
解释:
if ($request_method = 'OPTIONS') {...}
当请求方法为 OPTIONS 时:
添加允许源
Access-Control-Allow-Origin
为 * (可根据业务需要更改)
添加缓存时长
Access-Control-Max-Age
, 当下次请求时, 无需再发送 OPTIONS 请求
添加允许的方法, 允许的首部
添加一个内容长度为 0, 类型为
text/plain; charset=utf-8
, 返回状态码为
204
的首部
至此, 完成 OPTIONS 请求的正确响应.
Q2: 跨域请求正确响应
添加如下内容:
- location /example {
- if ($request_method = 'OPTIONS') {
- add_header Access-Control-Allow-Origin *;
- add_header Access-Control-Max-Age 1728000;
- add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
- add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
- add_header Content-Type''text/plain; charset=utf-8';
- add_header Content-Length 0 ;
- return 204;
- }
- + if ($http_origin ~* (https?://(.+\.)?(example\.com$))) {
- + add_header Access-Control-Allow-Origin $http_origin;
- + add_header Access-Control-Allow-Credentials true;
- + add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
- + add_header Access-Control-Expose-Headers Content-Length,Content-Range;
- + }
- proxy_redirect off;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_pass http://127.0.0.1:8080/;
- }
解释:
if ($http_origin ~* (https?://(.+\.)?(example\.com$))) {...}
, 当 origin 为合法域名 (可根据业务调整或去除合法域名验证) 时:
添加允许源
Access-Control-Allow-Origin
为 $http_origin (可根据业务需要更改)
添加允许认证
Access-Control-Allow-Credentials
为 true , 允许接收客户端 Cookie(可根据业务需要更改. 但要注意, 当设置为 true 时,
Access-Control-Allow-Origin
不允许设置为 *)
添加允许的方法, 暴露的首部
至此, 完成跨域请求正确响应.
以上, 是对跨域请求在 Web Server 的解决方案, 主要是通过响应 OPTIONS 方法和添加允许源来解决.
当然, 如果本地开发中, 可以在利用 webpack-dev-server 的 https://webpack.js.org/configuration/dev-server/#devserver-proxy 选项来快速解决跨域问题:
示例如下:
- // webpack.congf.js
- module.exports = {
- //...
- devServer: {
- proxy: {
- '/api': {
- target: 'http://localhost:3000',
- pathRewrite: {'^/api' : ''}
- }
- }
- }
- }
当访问地址如 /api/foo?q=bar 时, 则通过代理访问的实际地址是:
http://localhost:3000/foo?q=bar
来源: https://juejin.im/entry/5b474be1e51d4518e311710c