在成功搭建好DRF(Django rest framework)的Blog的backend後,昨天開始搭建Vue3+axios+pinia+element_plus的前臺服務. 開始一切順利,到第一個axios的get處理的時候,出現了錯誤. axios相關的代碼如下: 載入vue-axios和ax ...
在成功搭建好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引起的干擾.