实现cors跨域
cors
跨域是前端的一个老生常谈的问题,有很多文章都讲过相关的原理。
如果只看不做,则会停留在"八股文"的层面--记得快,忘得也快;
只有亲自配置过相关的 header,才能对 cors
有更深的理解。
下面,我们就来根据 MDN 的文档来实现 cors
跨域。
技术栈
为了方便开发,前后端使用的技术栈如下:
前端:axios
后端:koa2
简单请求
前端(端口号 8080,下同)
import axios from "axios";
res = await axios.post(
"http://localhost:3000/simple",
{},
{
headers: {},
}
);
后端(端口号 3000,下同)
router.get("/simple", async (ctx, next) => {
ctx.set("Access-Control-Allow-Origin", "http://localhost:8080");
ctx.body = "simple get response";
});
运行效果
可能出现的问题
如果不满足简单请求的条件,则浏览器会自动发送预检请求
复杂请求
options
请求是预检请求,由浏览器自动发出,请求头自动带上 Access-Control-Request-Headers
和 Access-Control-Request-Method
, 响应头需要加上 Access-Control-Allow-Origin
, Access-Control-Allow-Method
和 Access-Control-Allow-Headers
options
请求完成后,浏览器会检查响应头相关字段和正式请求是否对应。
预检请求、正式请求的相关 header
字段对应关系如下
options 请求头 | options 响应头 | 正式请求的请求头 | 正式请求的响应头 |
---|---|---|---|
Access-Control-Request-Headers | Access-Control-Allow-Headers | 字段必须要在 Access-Control-Allow-Headers 的范围内 | |
Access-Control-Expose-Headers定义的字段必须要出现在响应头中 | |||
Access-Control-Request-Method | Access-Control-Allow-Method | Request Method | |
Access-Control-Allow-Origin | Origin | Access-Control-Allow-Origin |
如果对应,则发出正式请求;
如果对应不上,则出现 cors
错误(无论 http 状态码是不是 20x)
对于正式请求,响应头也需要带上 Access-Control-Allow-Origin
,且值和预检请求的响应头一样。
实现代码如下
前端
res = await axios.post(
"http://localhost:3000/complex",
{},
{
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-cache",
},
}
);
后端
router.options("/complex", async (ctx, next) => {
ctx.set("Access-Control-Allow-Origin", "http://localhost:8080");
ctx.set("Access-Control-Allow-Method", "POST,PUT");
ctx.set("Access-Control-Allow-Headers", "Content-Type,Cache-Control");
ctx.set("Access-Control-Max-Age", 86400);
ctx.status = 204;
});
运行效果
常见的几个错误场景
1. 如果正式请求的请求头和预检请求的响应头对不上,正式请求就会出现 cors
错误,如下图所示
同时,浏览器会出现报错
Access to XMLHttpRequest at 'http://localhost:3000/complex' from origin 'http://localhost:8080' has been blocked by CORS policy: Method DELETE is not allowed by Access-Control-Allow-Methods in preflight response.
2. 如果正式请求的响应头缺少 Access-Control-Allow-Origin
字段,一样会报错
Access to XMLHttpRequest at 'http://localhost:3000/complex/file' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
3. 如果预检请求返回失败,正式请求一样会发出,然后出现 cors
错误
-
响应头有
Content-Coposition
,但是 js 代码中读取不到值下方的代码中, text的值是undefined
const res = await axios.post( "http://localhost:3000/complex/file", null, { headers: { "Content-Type": "application/octet-stream", }, responseType: "blob", } ); const text = decodeURI(res.headers["content-conposition"]);
打印response,结果如下:
原因
默认情况下只暴露安全的响应头,需要用 Access-Control-Expose-Headers
设置允许跨域暴露的响应头
developer.mozilla.org/zh-CN/docs/…
解决
在后端设置响应头
ctx.set("Access-Control-Expose-Headers", "Content-Conposition");
参考资料
转载自:https://juejin.cn/post/7350214909055369254