只是从http层角度说下跨域的问题,并不提供解决方法,解决方法在以前老的文章里写过
跨域
跨域的请求被拦截有两种可能:
1. 浏览器直接禁止发起跨域,例如在某些浏览器中HTTPS请求HTTP域请求不会发起;
2. 跨域请求发起了,但是返回结果被浏览器拦截,请求失败。
CORS
w3c协议
CORS是一种利用额外HTTP头部来允许用户代理跨域请求的技术,与传统跨域方式不同,CORS使得原本只允许调用同源资源的api(例如XMLHttpRequest与fetch)中进行跨域操作变得可能
XMLHttpRequest与fetch请求跨域资源时会默认使用CORS协议检查
CORS请求分为预检,正式发送两部分。测试库链接
预检
简单请求不发送预检请求
简单请求指:
1. 请求方法仅可为GET,HEAD或POST
2. 请求头部Content-Type仅可为text/plain,multipart/form-data或application/x-www-form-urlencoded
// 不触发预检
fetch(url).then(function(response) {
console.log(response);
});
// 触发预检
fetch(url, {
headers: {
'Content-Type': 'text/json'
}
}).then(function(response) {
console.log(response);
});
预检中不会带上触发了预检的请求头部,req.headers['content-type']
是无法获取到值的
用户代理(浏览器)在接收到预检请求回复后会根据返回结果判断,主要是根据ACAO等几个字段判断跨域请求是否可发起
因此,需要编写对应的options请求处理接口
router.options('/cors', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); // 允许请求头带content-type属性
res.send();
});
这样就可以通过预检
处理
预检通过后,浏览器会发送实际请求。
在最终的处理接口里也要加上ACAO等几项,否则用户代理也会认为跨域请求失败,不会将结果返回。具体内容参见mdn
router.get('/cors', (req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', true);
res.json(data);
}
});
需要说明的是,无论是简单请求还是通过了预检的跨域请求,都会将数据提交到服务器上并进行修改,无论服务器返回值会不会造成用户代理认为这次跨域请求失败
常见错误
- 请求带cookie时,回复中的
Access-Control-Allow-Origin
不能是*且Access-Control-Allow-Credentials
必须设置为true