Vue項目總結(3)-跨域問題分析

先後端分離的一個好處是能夠將前端和後端運行環境分開,各自進行管理和優化,增長了系統部署的靈活性和彈性。可是,這也帶來了瀏覽器跨域資源共享(CORS)問題,致使瀏覽器沒法訪問後端服務。本文搭建一個簡單示例(vue+json-server+nginx)說明該問題以及解決方法。html

什麼是CORS

跨域資源共享(CORS) 是一種機制,它使用額外的HTTP頭來告訴瀏覽器  讓運行在一個 origin (domain) 上的Web應用被准許訪問來自不一樣源服務器上的指定的資源。當一個資源從與該資源自己所在的服務器不一樣的域、協議或端口請求一個資源時,資源會發起一個跨域 HTTP 請求前端

好比,站點 domain-a.com 的某 HTML 頁面經過  的 src 請求 domain-b.com/image.jpg。網…vue

出於安全緣由,瀏覽器限制從腳本內發起的跨源HTTP請求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 這意味着使用這些API的Web應用程序只能從加載應用程序的同一個域請求HTTP資源,除非響應報文包含了正確CORS響應頭。ios

參考:developer.mozilla.org/zh-CN/docs/…nginx

演示項目

建立項目

vue create try-corsexpress

建立測試服務

在項目下建立放測試數據的目錄json-server,在目錄下建立文件db.json文件。npm

{
  "hello": {
    "id": 1,
    "msg": "你好,我是json-server"
  }
}
複製代碼

啓動模擬API服務,經過參數指定測試數據的位置和服務的端口。json

npx json-server json-server/db.json --port 4001axios

在瀏覽器地址欄中輸入http://localhost:4001/hello,檢驗是否啓動成功。後端

引入axios包,修改項目代碼

cnpm i axios -S

修改App.vue文件,引入axios。

<template>
  <div id="app">
    <button @click="sendRequest">發送請求</button>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'app',
  methods: {
    sendRequest() {
      let api_url = 'http://localhost:4001/hello'
      axios.get(api_url).then(rsp => {
        alert(JSON.stringify(rsp.data))
      })
    }
  }
}
</script>
複製代碼

運行

在瀏覽器中打開頁面,點擊【發送請求】按鈕,成功返回結果。這時並無由於跨域的問題致使請求失敗,這是由於json-server默認是開啓支持CORS。

啓動json-server時添加參數--no-cors--nc

npx json-server json-server/db.json --port 4001 --nc

在瀏覽器中打開頁面,打開【開發者工具】,點擊【發送請求】按鈕,查看請求執行的結果。

Access to XMLHttpRequest at 'http://localhost:4001/hello' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

注意:測試瀏覽器有可能會緩存結果,須要關閉緩存。

解決方案

有兩種解決方式:一、修改業務代碼,直接支持CORS;二、經過nginx進行代理,支持CORS。

修改後端代碼

json-server經過指定參數開關CORS就是一種後端解決方案。corsexpress的中間件(koa用的是@koa/cors),能夠設置各類CORS選項。下面代碼是一種最簡單的設置。

cors({
  origin: true,
  credentials: true
})
複製代碼

json-server支持CORS後,咱們在瀏覽器的開發者工具中觀察響應頭,會看到以下內容:

Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: http://localhost:8080

服務器端添加這兩個頭就是告訴瀏覽器,「我支持CORS」。

參考:www.npmjs.com/package/cor…

參考:www.npmjs.com/package/@ko…

利用nginx代理

關閉json-server對CORS的支持,將端口改成4002。

npx json-server --port 4002 --nc json-server/db.json

啓用nginx,開啓4001端口,反向代理到4001端口。

server {
  listen 4001;
  server_name	localhost;
  location / {
    proxy_pass http://localhost:4002;
  }
}
複製代碼

在瀏覽器中調用請求,返回失敗。

修改nginx配置文件,直接加頭。

server {
  listen 4001;
  server_name	localhost;
  location / {
    add_header Access-Control-Allow-Credentials true;
    add_header Access-Control-Allow-Origin http://localhost:8080;
    proxy_pass http://localhost:4002;
  }
}
複製代碼

Access-Control-Allow-Credentials 響應頭表示是否能夠將對請求的響應暴露給頁面。返回true則能夠,其餘值均不能夠。

參考:developer.mozilla.org/zh-CN/docs/…

前端代碼與運行環境解耦

弄清楚了CORS,咱們研究一下真實的部署問題。

須要和先後端代碼分離一同考慮的問題是,後端代碼的微服務化,也就是一個前端要訪問多個獨立部署的後端服務,例如:獨立的登陸鑑權服務,獨立的日誌服務。

image.png

Server1到Server4是4個獨立的服務環境,前端代碼部署在Server1,不一樣的後端服務分別部署在Server2到Server4,用戶經過瀏覽器訪問Server1得到前端代碼,瀏覽器中執行前端代碼訪問Server2-Server4。Server2到Server4是3個不一樣地址,前端代碼須要分別指定,這就致使前端代碼依賴運行環境的問題。

除了因爲先後端代碼單獨部署致使的代碼依賴運行環境,另外一個問題是內外網隔離。業務服務器都部署在內網,只暴露一個出口(互聯網ip+端口),這種狀況下,若是前端代碼中寫的是內網服務的地址確定訪問不了。

綜合先後分離和內外隔離兩方面的需求,編寫前端代碼時必須實現一種機制,避免在代碼中硬編碼後端服務地址,應支持根據具體的部署環境進行指定。

用vue開發時,能夠經過指定環境變量的方式實現上面的要求。

在項目的根目錄下建立.env.env.local等文件指定環境變量,注意環境變量必須以VUE_APP_開頭,例如:

VUE_APP_SERVER2=xxxx
VUE_APP_SERVER3=yyyy
VUE_APP_SERVER4=zzzz
複製代碼

在代碼中經過process.env訪問:

console.log("VUE_APP_SERVER2", process.env.VUE_APP_SERVER2)
console.log("VUE_APP_SERVER3", process.env.VUE_APP_SERVER3)
console.log("VUE_APP_SERVER4", process.env.VUE_APP_SERVER4)
複製代碼

這種方式並非在運行時使用環境變量,而是在編譯時用定義的值替換代碼中的環境變量。因此,針對不一樣的部署環境(能夠給不一樣環境指定不一樣名稱的env文件,具體參考官網文檔),指定相應的環境變量後,須要進行編譯造成和環境綁定的發佈版本。這樣就實現了編碼階段不須要硬編碼服務地址,編譯時再根據須要指定。

參考:cli.vuejs.org/zh/guide/mo…

總結

利用nginx能夠很好地解決跨域和內外網隔離問題,因此建議優先考慮採用nginx。若是徹底是在內網環境,能夠在後端服務上增長CORS支持。

編寫前端代碼時,應規劃好須要訪問哪些服務,經過設置環境變量,避免對後端地址硬編碼。

本系列其餘文章:

Vue項目總結(1)-基本概念+Nodejs+VUE+VSCode

Vue項目總結(2)-前端獨立測試+VUE

相關文章
相關標籤/搜索