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