首先通读下 MDN 关于 CORS 的 定义,了解跨域的含义及简单请求和复杂请求等的定义。文中的内容不赘述,现在说解决方案。
通过定义我们可以,简单请求与复杂请求的差别是复杂请求会自动发出一个 OPTIONS 的预检请求,当请求得到确认后,才开始真正发送请求。
综上,我们要解决两个问题:
OPTIONS 请求的正确响应
跨域请求正确响应
1、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 时:
1、添加允许源 Access-Control-Allow-Origin 为 * (可根据业务需要更改)
2、添加缓存时长 Access-Control-Max-Age,当下次请求时,无需再发送 OPTIONS 请求
3、添加允许的方法,允许的首部
4、添加一个内容长度为0,类型为 text/plain; charset=utf-8 , 返回状态码为 204 的首部
至此,完成 OPTIONS 请求的正确响应。
2、跨域请求正确响应
添加如下内容:
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 为合法域名(可根据业务调整或去除合法域名验证)时:
1、添加允许源Access-Control-Allow-Origin为 $http_origin (可根据业务需要更改)
2、添加允许认证Access-Control-Allow-Credentials为 true ,允许接收客户端 Cookie(可根据业务需要更改。 但要注意,当设置为true时,Access-Control-Allow-Origin 不允许设置为 *)
3、添加允许的方法,暴露的首部
至此,完成跨域请求正确响应。
以上,是对跨域请求在Web Server的解决方案,主要是通过响应 OPTIONS 方法和添加允许源来解决。
当然,如果本地开发中,可以在利用 webpack-dev-server 的 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