为何Axios触发Access-Control-Allow-Origin错误及解决方法
问题根源分析
你遇到的问题核心在于Axios的全局默认头触发了浏览器的CORS预检请求(OPTIONS),而目标API服务器没有正确处理这个预检请求,但原生XMLHttpRequest因为没携带自定义头,属于「简单请求」,不需要预检,所以能正常工作。
具体拆解:
- 你给Axios全局设置了
authorization和content-type: application/json头:
这两个配置直接让你的GET请求变成非简单请求:axios.defaults.headers["content-type"] = "application/json"; axios.defaults.headers.common.authorization = `Bearer ${token}`;Authorization属于自定义请求头,不在浏览器规定的简单请求头列表内;content-type: application/json也超出了简单请求允许的application/x-www-form-urlencoded、multipart/form-data、text/plain范围。
- 对于非简单请求,浏览器会先发送OPTIONS预检请求,询问服务器是否允许该请求的来源、头信息等。但目标API的服务器没有返回
Access-Control-Allow-Origin头,也未允许Authorization或application/json这类自定义头,所以浏览器直接拦截请求,抛出CORS错误。 - 而你写的原生XHR代码没有添加任何自定义头,属于简单请求,浏览器不会发送预检,只要服务器返回的响应包含
Access-Control-Allow-Origin(哪怕是通配符*)就能被浏览器接受,因此正常工作。
修复方案
根据你的需求,有几种可行的修复方式:
方案1:移除不必要的全局默认头
既然目标API不需要Authorization和application/json头,直接删掉全局配置里的这两行即可:
// 删掉这两行全局配置 // axios.defaults.headers["content-type"] = "application/json"; // axios.defaults.headers.common.authorization = `Bearer ${token}`;
之后Axios会使用默认请求头,发送的GET请求就会变成简单请求,不会触发预检,也就不会有CORS错误了。
方案2:在单个请求中覆盖全局头
如果你需要保留其他请求的全局头配置,仅在这个API请求中移除自定义头,可以在请求时单独设置headers来覆盖:
axios.get(GEO_IP, { headers: { // 覆盖全局的Authorization头,不携带它 authorization: undefined, // 覆盖content-type,使用简单请求允许的类型 "content-type": "text/plain" } }) .then(res => res) .catch(err => err);
这样这个特定的GET请求就不会携带触发预检的头,变回简单请求。
方案3:配置Axios实例隔离全局配置
如果你的项目中有多个API需要不同的头配置,建议创建单独的Axios实例来处理这个公开API:
// 创建一个不带自定义头的Axios实例 const publicAxios = axios.create(); // 移除实例的默认自定义头 delete publicAxios.defaults.headers["content-type"]; delete publicAxios.defaults.headers.common.authorization; // 使用这个实例请求公开API publicAxios.get(GEO_IP) .then(res => res) .catch(err => err);
这种方式可以避免全局配置影响到不需要自定义头的请求,更适合复杂项目的场景。
内容的提问来源于stack exchange,提问作者Michael Lonin
相关产品推荐
相关产品推荐

