axios 跨域_当遇到跨域开发时,我们如何处理好前后端配置和请求库封装
我們知道很多大型項目都或多或少的采用跨域的模式開發(fā), 以達到服務(wù)和資源的解耦和高效利用. 在大前端盛行的今天更為如此, 前端工程師可以通過nodejs或者Nginx輕松搭建起web服務(wù)器.這個時候我們只需要請求后端服務(wù)器的接口即可實現(xiàn)系統(tǒng)的業(yè)務(wù)功能開發(fā).這個過程中會涉及到web頁面向API服務(wù)器的跨域訪問(由于受到瀏覽器的同源策略,但是業(yè)界已有很多解決方案,接下來會介紹).通過這種開發(fā)模式使得我們真正的實現(xiàn)了前后端完全分離.
采用這種前后端單獨開發(fā)部署的模式好處有如下幾點:
- 減少后端服務(wù)器的并發(fā)/負(fù)載壓力
- 前端項目和后端項目完全分離, 一定程度上提高了自動化部署的靈活性, 并且代碼更易管理和維護
- 提高前后端開發(fā)團隊的工作效率, 各司其職, 出現(xiàn)bug更容易定位問題
- 在大并發(fā)情況下可以同水平擴展前后端服務(wù)器,利用多臺前端服務(wù)器做集群來抗住日均千萬級的pv
- 提高應(yīng)用容錯, 即使是API服務(wù)器掛了, 前端頁面依然能正常訪問
- API服務(wù)器能同時為多個應(yīng)用平臺提供服務(wù), 大量復(fù)用接口,提升效率。(比如說微服務(wù))
雖然好處有很多, 但是為了實現(xiàn)以上的架構(gòu)模式, 我們首先要解決的就是跨域問題.
瀏覽器的同源策略
同源策略是一個重要的安全策略,它用于限制一個origin的文檔或者它加載的腳本如何能與另一個源的資源進行交互。它能幫助阻隔惡意文檔,減少可能被攻擊的媒介。
如果兩個URL的protocol(協(xié)議,比如http協(xié)議,https協(xié)議)、port (端口號,如80)和 host(主機,如developer.mozilla.org) 都相同的話,則這兩個 URL 是同源。這個方案也被稱為“協(xié)議/主機/端口元組”,或者直接是 “元組”。也就是說如果不滿足以上3個條件中的任意一個,則被視為跨域.
解決跨域問題的幾種方式
業(yè)界解決瀏跨域問題的方案很多, 筆者在這里粗略介紹一下:
- JSONP實現(xiàn)跨域 通過script標(biāo)簽和url回調(diào)來實現(xiàn)跨域, 缺點是只支持get請求
- CORS CORS需要瀏覽器和后端同時支持, 后端設(shè)置Access-Control-Allow-Origin 就可以開啟 CORS
- postMessage 可以實現(xiàn)跨文本檔、多窗口、跨域消息傳遞(筆者之前寫可插拔式聊天機器人就是采用該方案)
- websocket websocket是HTML5的一個持久化的協(xié)議,它實現(xiàn)了瀏覽器與服務(wù)器的全雙工通信,也是跨域的一種解決方案
- nginx反向代理
- document.domain + iframe 比較傳統(tǒng)的跨域解決方案
目前作為大規(guī)模跨域開發(fā)使用最多的模式還是CORS方案,所以筆者接下來將具體介紹采用cors模式搭建前后端跨域訪問通用解決方案, 為了方便,筆者后端將采用nodejs+koa, (java/php開發(fā)類似), 前端采用axios作為請求庫來配合實現(xiàn)完整的cors模式.
跨域開發(fā)的后端配置(node/koa版)
要想徹底了解cors的跨域模式, 我們還是要深入實踐中來, 筆者將采用nodejs和koa中間件來實現(xiàn)cors模式的搭建.這里筆者先簡單介紹一下cors:
跨域資源共享(CORS) 是一種機制,它使用額外的 HTTP 頭 來告訴瀏覽器 讓運行在一個域上的Web應(yīng)用被準(zhǔn)許訪問來自不同源服務(wù)器上指定的資源。
基本場景如下:
對于簡單的跨域場景,我們只需要設(shè)置請求頭的Access-Control-Allow-Origin字段即可, 比如設(shè)置為*號表示允許任何域名的訪問.
這里我們使用koa2-cors這個中間件來實現(xiàn)一下, 代碼如下:
import koa from 'koa';import cors from 'koa2-cors';const app = new koa();// 設(shè)置跨域app.use(cors({ origin: function (ctx) { return '*' }}))復(fù)制代碼通過這樣的配置, 我們就能輕松實現(xiàn)cors跨域, 不過現(xiàn)實開發(fā)中我們一般不會這么設(shè)置, 因為這樣設(shè)置意味著任何人都能訪問我們的服務(wù),安全性無法保證. 作為小型的開放服務(wù),可以采用這樣的配置加上訪問限流來實現(xiàn)免費圖床類應(yīng)用.(開放圖床實現(xiàn)可以參考筆者之前寫的文章使用nodeJs開發(fā)自己的圖床應(yīng)用)
在實際開發(fā)中, 我們會將origin的返回值設(shè)置為指定域名, 這樣就只允許該域名下的請求訪問, 所以正確的姿勢如下:
import koa from 'koa';import cors from 'koa2-cors';const app = new koa();const isDev = process.env.NODE_ENV === 'development';// 設(shè)置跨域app.use(cors({ origin: function (ctx) { return isDev ? '*' : 'http://qutanqianduan.com' }}))復(fù)制代碼通過這種方式, 我們在開發(fā)環(huán)境中, 可以讓前端同事自由訪問我們的API接口, 提高聯(lián)調(diào)效率, 而在生產(chǎn)環(huán)境中只允許我們的WEB服務(wù)器所在域名訪問.
更進一步
對于簡單請求和簡單的開發(fā)模式, 以上的設(shè)計就基本滿足要求了, 但是對于復(fù)雜的業(yè)務(wù)場景, 我們的請求模式往往會涉及到更多的要求, 比如說需要攜帶cookie, 用戶憑證或者自定義的請求頭信息等(比如典型的JWT認(rèn)證的token一般會存放到自定義的頭信息中), 此時往往會發(fā)送預(yù)檢請求(要求必須先使用 OPTIONS 方法發(fā)起一個預(yù)檢請求到服務(wù)器,以獲知服務(wù)器是否允許該實際請求。"預(yù)檢請求“的使用,可以避免跨域請求對服務(wù)器的用戶數(shù)據(jù)產(chǎn)生未預(yù)期的影響).
這里我們需要了解以下幾個響應(yīng)頭部的字段:
- Access-Control-Allow-Methods 表明服務(wù)器允許客戶端使用的請求方法
- Access-Control-Allow-Headers 表明服務(wù)器允許請求中攜帶的頭部字段
- Access-Control-Max-Age 表明響應(yīng)的有效時間。在有效時間內(nèi),瀏覽器無須為同一請求再次發(fā)起預(yù)檢請求
- Access-Control-Expose-Headers 服務(wù)器允許瀏覽器訪問的頭信息白名單
- Access-Control-Allow-Credentials 指定了當(dāng)瀏覽器的credentials設(shè)置為true時是否允許瀏覽器讀取response的內(nèi)容
以上這5個響應(yīng)頭部字段非常重要,這也是我們解決復(fù)雜跨域場景的關(guān)鍵配置. 具體配置案例如下:
// 設(shè)置跨域app.use(cors({ origin: function (ctx) { if (ctx.url.indexOf(config.API_VERSION_PATH) > -1) { return isDev ? 'http://192.xxx.1.3:8000' : 'http://qutanqianduan.cn'; // 允許來自指定域名請求, 如果設(shè)置為*,前端將獲取不到錯誤的響應(yīng)頭 } }, exposeHeaders: ['WWW-Authenticate', 'Server-Authorization', 'x-show-msg'], maxAge: 5, // 該字段可選,用來指定本次預(yù)檢請求的有效期,單位為秒 credentials: true, // 允許攜帶用戶憑證 allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // 允許的請求方法 allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'X-Requested-With'] // 允許接收的頭部字段}))復(fù)制代碼以上是采用koa2-cors實現(xiàn)的方案, 通過設(shè)置exposeHeaders, 我們就可以在瀏覽器端拿到服務(wù)器響應(yīng)的頭部字段'WWW-Authenticate', 'Server-Authorization', 'x-show-msg', 進而根據(jù)這些字段的值來實現(xiàn)定制化的消息機制.
需要注意的是, 我們服務(wù)器在設(shè)置credentials后,需要前端請求庫配置設(shè)置,比如我們需要在axios中設(shè)置withCredentials為true, 代碼如下:
import axios from 'axios'const isDev = process.env.NODE_ENV === 'development'const instance = axios.create({ baseURL: isDev ? 'http://localhost:3000/api/xxx' : 'http://localhost/api/xxx', withCredentials: true});復(fù)制代碼這樣我們就能成功攜帶用戶憑證并被跨域的后端服務(wù)器獲取了.以上就實現(xiàn)了我們cors模式的后端配置, 對于nodeJS為主的后端選手, 基本任務(wù)已經(jīng)完成, 對于java/PHP選手, 也可以參考類似的配置和庫來實現(xiàn). 接下來我們來實現(xiàn)前端請求庫的封裝.
跨域開發(fā)的前端請求庫封裝(axios版)
作為一名前端工程師, 沒有一個上手的請求庫是萬萬不行的, 目前業(yè)界比較好的輪子有axios, umi-request等, 但是后者在使用過程中有一些坑(畢竟基于fetch實現(xiàn)), 所以這里筆者將基于axios來簡單實現(xiàn)一個跨域請求庫的封裝.方便大家集成在自己的vue或者react項目中. 接下來看看請求庫封裝的簡單模型:
筆者將基于http規(guī)范的錯誤類型進行基本的消息系統(tǒng)設(shè)計, 代碼如下:
import axios from 'axios'import { message } from 'antd'const isDev = process.env.NODE_ENV === 'development'const instance = axios.create({ baseURL: isDev ? 'http://localhost:3000/api/xxx' : 'http://qutanqianduan/api/xxx', timeout: 10000, withCredentials: true});// 添加請求攔截器instance.interceptors.request.use(function (config) { // 在發(fā)送請求之前做些什么 config.headers = { 'x-requested-with': localStorage.getItem('user') || '', 'authorization': localStorage.getItem('token') || '' } return config; }, function (error) { // 對請求錯誤做些什么 return Promise.reject(error); });// 添加響應(yīng)攔截器instance.interceptors.response.use(function (response) { // 對響應(yīng)數(shù)據(jù)做點什么 if(response.headers['x-show-msg'] === 'zxzk_msg_200') { message.success(response.data.msg); } return response.data.result; }, function (error) { // 對響應(yīng)錯誤做點什么 const { response } = error; if(response.status === 404) { message.error('請求資源未發(fā)現(xiàn)'); }else if(response.status === 403) { message.error(response.data.msg, () => { window.location.href = '/login' }); }else { message.error(response.data.msg); } return Promise.reject(error); });export default instance復(fù)制代碼以上筆者結(jié)合antd的message作為消息反饋UI,利用axios的請求和響應(yīng)攔截來實現(xiàn)消息系統(tǒng)的設(shè)計, 以上只是基本的框架, 大家可以基于以上設(shè)計進行更加自定義的封裝.
講到這里, 大家是不是對跨域下的服務(wù)端和前端配置有了更進一步的了解了呢?
最后
如果想學(xué)習(xí)更多H5游戲, webpack,node,gulp,css3,javascript,nodeJS,canvas數(shù)據(jù)可視化等前端知識和實戰(zhàn),歡迎在《趣談前端》學(xué)習(xí)討論,共同探索前端的邊界。
總結(jié)
以上是生活随笔為你收集整理的axios 跨域_当遇到跨域开发时,我们如何处理好前后端配置和请求库封装的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 熬过这段时间励志话语83句
- 下一篇: axure 小程序 lib_小程序定制开