Access-Control-Allow-Origin 跨域资源请求报错解决方案

本文介绍了在uni-app前端开发中遇到的CORS跨域问题及其解决方法。通过理解CORS的工作原理,提出了设置代理服务器来规避浏览器的CORS策略。详细讲述了在vue.config.js中配置devServer代理及注意事项,包括如何处理manifest.json覆盖问题。

做uni-app前端开发时,遇到CORS策略问题:

:8081/#/pages/index/index:1 Access to XMLHttpRequest at 'http://api-remote.xxxx.com'/version/getCurrentVersion' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

官网给的解决方案在这里:
https://juejin.cn/post/6844904063855755271#heading-8
但是这个帖子讲的不是很明白,至少刚入前端的我没看懂。。然后发现vue教程中反而解释的非常清楚。由于uni-app也是通过vue-cli创建,因此可以使用同样的解决方案,只需要增加一个文件并修改一行请求代码。可以直接跳到末尾看解决方法

问题原因

CORS即Cross-origin resource sharing,跨域资源共享,这个技术是为了解决AJAX的同源策略影响(需要域名(或IP),协议,端口都相同)。详细介绍参考阮一峰-跨域资源共享 CORS 详解
简单来说,对于跨域请求和除了GET、HEAD外的同源请求,现代浏览器会自动在请求头中加入一个Origin字段。Origin字段包含本次请求的协议+域名+端口,如

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

而服务器如果开启了CORS端口,并且同意该Origin的请求,就会在回复头中添加Access-Control开头的几个字段:

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

其中Access-Control-Allow-Origin是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
如果返回的头部没有这个字段,浏览器会认为请求出错,错误被XMLHttpRequest的onerror回调函数捕获,这就是我们遇到的浏览器报错。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。
注意:CORS是浏览器策略,与服务器请求无关
开启vue项目后,本地会开启一个localhost:8081服务器,从这个服务器向外部的域名发送请求属于跨域请求,后端没有配置CORS因此不会有Access-Control-Allow-Origin头。
而我们前端代码中的请求实际调用的是浏览器window的xhr方法,所以一定会受到浏览器的CORS策略拦截。具体什么时候受到CORS策略影响,参考什么情况下需要 CORS .

解决思路

  1. 开启一个代理服务器,让代理服务器假装与请求的域名同源,绕过浏览器的CORS策略。
  2. 关闭浏览器的CORS拦截,这里不讨论。

解决方法

1、在项目根目录创建vue.config.js文件,配置devServer,即开发代理服务器

// vue.config.js
module.exports = {
  devServer: {
    proxy: {
     	// 属性名可以是自定义的一个端口目录,为了让代理服务器区分是请求本地服务器还是目标服务器
      '/prefix': { 
        target: 'http://api-remote.xxxx.com', //这里写请求目标网站的协议+域名(IP)+端口,默认是80端口
        pathRewrite: {
          '^/prefix': '' // 自动去除/prefix这个自定义的端口目录前缀,这样在不修改后端api的情况下可以做到前端的api管理
        }
      }
    },
  }
}

2、改写原来的请求,简写为只保留/prefix和之后的内容。默认不加前缀就是向开启当前页面的服务端发送请求。

//uni.request({
//	url: "http://api-remote.xxxx.com/version/getCurrentVersion",
//	...
//	})
uni.request({
	url: "/version/getCurrentVersion",
	...
	})

另一种方式是改写原来的请求目标为本地的代理服务器,即vue项目运行的本地端口(http://localhost:8081)。代理服务器会转发请求到上一步配置的target。
但这样在部署到后端之前还要改回原来的写法,因为后端是没有代理服务器的。所以还是采用简写的方式更好。

//uni.request({
//	url: "http://api-remote.xxxx.com/version/getCurrentVersion",
//	...
//	})
uni.request({
	url: "http://localhost:8081/version/getCurrentVersion",
	...
	})

3、重启vue项目。这一步很重要!不然配置不会生效。

注意事项

如果配置了manifest.json,其中的h5项会覆盖vue.config.js。这种情况下需要使用https://juejin.cn/post/6844904063855755271 提到的第一种方法,在h5属性中写入devServer配置:

// manifest.json
    "h5" : {
        "devServer" : {
            "proxy" : {
                "/version" : {
                    "target" : "http://47.98.232.98:8083"
                }
            },
            "https" : false
        },
        "title" : "download_app",
        "router" : {
            "mode" : "hash",
            "base" : "./"
        }
    },

注意这里每个属性名都要加引号。
本人实践时不知道改动了什么,本来vue.config.js用得好好的,重启之后又报跨域错误,无奈还是去修改了manifest.json。

—更新—
破案了,不知为何把vue.config.js改成了vue.configjs,所以配置失效了。
粗心大意的毛病何时能消失orz

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值