漏了一个“/”导致的跨域错误(CORS)
在成功搭建好DRF(Django rest framework)的Blog的backend后,昨天开始搭建Vue3+axios+pinia+element_plus的前台服务.
开始一切顺利,到第一个axios的get处理的时候,出现了错误.
axios相关的代码如下:
加载vue-axios和axios模块
npm install --save vue-axios axios
axios初始化(main.ts)
app.use(VueAxios, axios); axios.defaults.baseURL = "http://localhost:8000/api"; //axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*'; // axios.defaults.headers.common['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'; //axios.defaults.headers.common['Access-Control-Allow-Headers'] = 'X-Requested-With,content-type'; //axios.defaults.headers.common['Content-Type'] = 'application/x-www-form-urlencoded'; app.provide('axios',app.config.globalProperties.axios)
axios取数据
在Pinia的Store中调用axios.get方法取得Backend端的Tag数据
export const useBlogData = defineStore("blogData",()=>{ const axios:any = inject('axios'); const tagsMenu = ref([]) axios.get('/tags').then((response:{data:any})=>{ tagsMenu.value = response.data.results; }) return {tagsMenu} })
目前的代码不严谨,没有进行错误处理.
出现跨域错误(CORS)
在Chrome的开发者模式下可以看到,报告Cors错误(CORS policy: No ‘Access-Control-Allow-Orign’ header is present on requested resource.
发现错误后,急急慢慢去Google解决方案, 了解CORS的出错的原因以及可能的解决方案,
CORS跨域错误的原因
MDN上有文章详细说明了CORS出错的原因以及Client Server端的请求与应答之间的关系.https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
简单的来说,我们的backend与frontend服务是分开的, Browser在执行Vue相关的处理的时候,回交叉访问不同的服务器,这样对于服务器来说可能会出现安全漏洞,所以会对这种请求进行拒绝操作.
关于CORS的中文的说明在Google云上有一篇文章说明https://cloud.google.com/storage/docs/cross-origin?hl=zh-cn, 我觉得还是MDN上讲解的更透彻.
CORS跨域错误的解决方案
在Google、StackOverflow上各种查询最终得出应该是两种解决方案:
Backend Server端的解决方案
Django提供了Backend Server端的解决方案,在前面的文章中已经提到过处理方法:
参考Django的CORS Header模块说明 https://pypi.org/project/django-cors-headers/
安装django-cors-headers, 修改setting.py, 其中需要注意的是MiddleWare相关的修改
MIDDLEWARE = [ ..., "corsheaders.middleware.CorsMiddleware", "django.middleware.common.CommonMiddleware", ..., ]
corsheaders.middleware.CorsMiddleware的位置尽量向上,要在这个CommonMiddleware前面,否则也可能会出现CORS错误.
我的Backend端一直有这样的处理,检查代码,加入各种奇怪的配置,还是继续出错,苦恼很长时间.
Frontend Sever的解决方案
Frontend Server的解决方案就是在Frontend Server上设置Proxy, 将Vue的axios发起的get请求不直接发给Backend Server,发给Frontend, Frontend通过Proxy的配置转给Backend Server,从而避免了CORS的问题.
我的开发环境使用的是Vite, Google等上面有说修改vue.config.ts的,这个主要是面向Vue-CLI的,在Vite上修改的vite.config.ts
server:{ port:8080, proxy:{ '/api':'http://127.0.0.1:8000' } },
加入proxy代理, 如上所示我的backend端的rest api的url是http://127.0..0.1:8000/api ,加入上述处理后 Frontend对应的http://localhost:8080/api上的请求会通过proxy转移到backend上
在axios的处理中,将面向backend处理baseurl设置为frontend上
axios.defaults.baseURL = "http://localhost:8080/api";
通过Frontend Server的解决方案可以成功解决目前碰见的CORS问题.
为什么Backend Server端的解决方案没有解决问题呢?
虽然通过Frontend Server的解决方案绕过了CORS错误,但是为什么Backend Server端的解决方案就是不行呢?
Google、Stackoverflow给出了在http的header上加Access-Control-Allow-Origin的解题过程.
我在代码中加入headers相关的处理
axios.defaults.baseURL = "http://localhost:8000/api"; axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
Chrome上的错误出现了新的情况:变成 access-control-allow-orign is not allowed by Access-Control-Headers的错误
在Chrome 网络中可以看见多了一个axios的请求成功了, axios发了一个preflight的请求去Backend预检测是否可以跨域请求
发现preflight请求成功了,我以为自己离解决问题近了一步,现在反思起了其实是退步了. 之后一直陷在这个地方找不到原因,各种Server端的配置,Frontend端的配置都不能成功.
在Stackoverflow上发现有人说问题就是结尾少了一个“/”, 我在axios的get请求“/tags”加入“/tags/”, 仍然还是出错.一度错过了这个问题的解决方案.
解决问题
折腾一晚上睡觉起了,再仔细的看MDN中的文章, 文章中说对于Simple Request(get、post、put、delete)的请求是不需要发送preflight请求的.现在看这个错误的意思就是access-control-allow-orgin这个header字段不被Backend Server支持,Backend拒绝了访问. 看不到解决方案决定回到起点,将header相关的处理去掉,将axios的代码回归到
axios.defaults.baseURL = "http://localhost:8000/api"; //axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
通过curl -i -L查看请求的结果,发现Backend端其实已经取得了tags数据,并返回给Broswer,但是结果确被重定向了,会出现301错误
这时候想起看过的关于请求结尾需要加“/”的处理,想明白了可能就是这个“/”导致的错误.在axios的get请求中将“/tags”,修改为“/tags/”.问题得到解决.
修改代码
export const useBlogData = defineStore("blogData",()=>{ const axios:any = inject('axios'); const tagsMenu = ref([]) axios.get('/tags/').then((response:{data:any})=>{ tagsMenu.value = response.data.results; }) return {tagsMenu} })
最终的解决方案: Backend的cors header模块 + “/”解决问题.
还有一个需要注意的地方是,在每次修改代码后,最好是将之前的页面关掉,重开避免Cache引起的干扰.