问题背景
Vue项目里用axios调用一个公共开放的API服务(该服务已经设置Access-Control-Allow-Origin:*)
问题表现
- 在axios服务里面的代码(部分逻辑代码已省略)
// request.js
……
axios.defaults.withCredentials = true;
axios.defaults.crossDomain = true;
axios.interceptors.request.use(config => {
……
config.headers['Authorization'] = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}`;
config.headers[website.tokenHeader] = 'bearer ' + getToken()
config.headers["Content-Type"] = "text/plain";
……
})
……
// index.js
export const getData = () => request({
url: 'http://location.tianditu.gov.cn/data/getCityName?callback=query',
method: 'get'
});
// index.vue
getData().then(res => {
const data = res.data;
})
- 在axios服务的报错信息
- 请求头信息
明明是开放的第三方公共API,用postman调用人家的Access-Control-Allow-Origin已经设置了*,却显示跨域的报错信息?我表示不服,于是三下五除二在当前项目引入了JQuery,用$大法发起ajax请求
$.ajax({
type:"get",
url:"http://location.tianditu.gov.cn/data/getCityName?callback=query",
success: function(data) {
console.log(data)
}
});
初步结论
这个带些玄学色彩的接口,用JQuery大法请求是正常的,用axios请求报跨域
于是我就各种谷歌,依然毫无头绪,有些大佬也遇到过这样的问题,提出来过但是并没有给除一个完美的解释,就在我准备放弃的时候,突然不小心扫到阮一峰老师五年前的一篇跨域资源共享 CORS 详解
浏览器的两种请求
浏览器将请求分为两大类:简单请求(simple request)和非简单请求(not-so-simple request)
-
简单请求(需要同时满足以下两大条件)
- 请求方法是以下三种方法之一
- HEAD
- GET
- POST
- HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
- 请求方法是以下三种方法之一
-
非简单请求(凡是不是简单请求的,就是非简单请求)
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或者DELETE,或者content-type是application/json。非简单请求会在正式通信之前,增加一次HTTP的查询请求,称为“预检”请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的
XMLHttpRequest
请求,否则就报错。
哥德巴赫猜想
axios跨域是因为用axios发起了一个非简单请求,而JQuery大法只是一个简单的请求,跨域正是因为非简单请求的preflight
axios服务发起的非简单请求
$.ajax发起的简单请
总结
之前封装的axios服务,带来太多非必要的请求头信息,导致简单请求变成非简单请求,非简单请求又发起了了一个preflight,而该第三方服务对预检请求处理可能出了些问题,导致浏览器误认为这是一次跨域的请求
还是之前的axios服务,剔除掉冗余的header信息或者用一个new的axios,依然可以愉快的正常发起简单请求