Access-Control-Allow-Origin 相关
问题出现
No ‘Access-Control-Allow-Origin’ header is present on the requested resource,但是 status 200 OK
Access to XMLHttpRequest at 'http://xxx/get' from origin 'http://ccc' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
这种现象是服务器端后台允许 OPTIONS 请求,并且接口也允许 OPTIONS 请求,但是头部匹配时出现不匹配现象.
比如 origin 头部检查不匹配,比如少了一些头部的支持(如常见的 X-Requested-With 头部),然后服务端就会将 response 返回给前端,前端检测到这个后就触发 XHR.onerror,导致前端控制台报错.
解决办法
增加跨域中间件:
import { Context } from "koa";
/**
* 跨域白名单
*/
enum WhiteList {
DEV = 'http://dev-xxx.com:6666',
TEST_HTTP = 'http://test.com',
TEST_HTTPS = 'https://test.com',
PROD_HTTP = 'http://prod.com',
PROD_HTTPS = 'https://prod.com',
}
/**
* 跨域中间件
* @param ctx 上下文拿到Origin来判断是否是跨域请求
* @param next 如果是跨域就设置允许的源和Headers,再放行
*/
export async function CORSMiddleware(ctx: Context, next: (err?: any) => Promise<any>): Promise<any> {
// 获取 Origin 请求头,只有非简单请求时,浏览器才会带上Origin字段来标识
const requestOrigin = ctx.get('Origin');
// 不管有没有跨域都要设置 Vary: Origin
ctx.set('Vary', 'Origin')
const whiteList = Object.values(WhiteList) as string[]
// 1.如果没有设置,说明没有跨域,跳过
// 2.或者 不在域名白名单中这直接跳过
if (!requestOrigin || !whiteList.includes(requestOrigin)) {
return await next();
}
// 设置响应头
ctx.set('Access-Control-Allow-Origin', requestOrigin)
// 跨域解决
ctx.set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
// 该字段可选,用来指定本次预检请求的有效期,单位为秒。
// 当请求方法是PUT或DELETE等特殊方法或者Content-Type字段的类型是application/json时,服务器会提前发送一次请求进行验证
// 下面的的设置只本次验证的有效时间,即在该时间段内服务端可以不用进行验证
ctx.set("Access-Control-Max-Age", '86400');
return await next();
}