跨域实例和解决方案
以下我们用 nodejs 演示了一个 http 服务, 接口为 http://localhost:8000/
.如果正常连接成功, 则会返回文本 ok
.
然后以 js 在浏览器中进行 fetch 进行请求测试, 演示各种形式的跨域, 以及对应的解决方式, 所有代码都是可运行的实例.
服务端代码, 默认没有跨域
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, {
'x-age': '18',
});
res.end('ok');
});
server.listen(8000, () => console.log(`启动成功 http://localhost:8000`));
客户端代码报错以及对应的设置方式
/**
简单请求
await fetch('http://localhost:8000/api/test').then(res => res.text())
报错
Access to fetch at 'http://localhost:8000/api/test' from origin 'http://localhost:9005' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
设置响应头
'Access-Control-Allow-Origin': '*'
*/
/**
自定义请求头
await fetch('http://localhost:8000/api/test ', {
headers: { 'Content-Type': 'application/json' },
}).then(res => res.text())
错误
Access to fetch at 'http://localhost:8000/api/test' from origin 'http://localhost:9005' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
设置响应头
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
*/
/**
自定义方法
await fetch('http://localhost:8000/api/test ', {
method: 'PATCH',
}).then(res => res.text())
错误
Access to fetch at 'http://localhost:8000/api/test' from origin 'http://localhost:9005' has been blocked by CORS policy: Method PATCH is not allowed by Access-Control-Allow-Methods in preflight response.
设置响应头
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
*/
/**
携带 cookie
await fetch('http://localhost:8000/api/test', {
credentials: 'include',
}).then(res => res.text())
报错
Access to fetch at 'http://localhost:8000/api/test' from origin 'http://localhost:9005' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
Access to fetch at 'http://localhost:8000/api/test' from origin 'http://localhost:9005' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'.
设置响应头
'Access-Control-Allow-Origin': 'http://localhost:9005',
'Access-Control-Allow-Credentials': 'true',
*/
/**
设置自定义 header
await fetch('http://localhost:8000/api/test', {
headers: { 'x-age': '28' },
}).then(res => res.text())
报错
Access to fetch at 'http://localhost:8000/api/test' from origin 'http://localhost:9005' has been blocked by CORS policy: Request header field x-age is not allowed by Access-Control-Allow-Headers in preflight response.
设置响应头
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
*/
/**
读取自定义 header
await fetch('http://localhost:8000/api/test').then(res => res.headers.get('x-age'))
报错
无, 但不能读取到 header 中的 x-age
设置响应头, 表明允许获取的自定义头
'Access-Control-Allow-Origin': '*',
'Access-Control-Expose-Headers': 'x-age',
*/
/**
综合测试
await fetch('http://localhost:8000/api/test', {
method: 'PATCH',
credentials: 'include',
headers: { 'x-age': '28' },
body: JSON.stringify({
token: 'test_token',
content: 'test_content'
})
}).then(async res => ({text: await res.text(), age: res.headers.get('x-age')}))
*/
总结
是否可以跨域, 是由服务器端决定的. 通常设置响应头即可, 在响应头中告诉浏览器允许跨域的域名, 以及允许跨域的方式, 还有允许读取和设置的请求头, 全都由服务端决定.
一般我们有几种解决方案:
- 关闭浏览器的安全策略
- 安装浏览器插件
- 直接从后端代码上设置为允许
- 使用代理方式
其中插件的方式和代理的方式其他都是在中途拦截并设置 header 为对应的跨域标致.
在前端项目的开发环境中, 一般可以使用 webpack 的 proxy 功能, 或者可以使用 mockm 的 proxy 功能. 推荐 mockm, 因为它不仅可以代理, 并且不需要复杂的配置, 还能获取到请求的接口历史和数据.