漏了一个“/”导致的跨域错误(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引起的干扰.

 

 

热门相关:无量真仙   仗剑高歌   网游之逆天飞扬   刺客之王   网游之逆天飞扬