優點
- 統一維護管理接口;
- 支持接口代理轉發;
- 讀取環境配置,區分處理環境。
- 攔截請求和響應,處理登錄超時、404 等異常情況;
- 根據請求的配置匹配接口 URL 前綴且作支持做特殊處理。
封裝
安裝 axios
npm install axios;
創建實例
通過創建實例,操作實例的方式進行接口請求。
//request.js import axios from 'axios'; // 引入axios import Qs from 'qs'; // 引入qs模塊,用來序列化post類型的數據 import { autoMatch, checkStatus } from './utils'; // 處理函數 import { Toast } from 'mint-ui'; //提示框 // 創建axios實例 const instance = axios.create({ // baseURL: process.env.BASE_URL, timeout: 30000, // 請求超時時間 // `transformRequest` 允許在向服務器發送前,修改請求數據 transformRequest: [function (data) { // 對 data 進行任意轉換處理 return data; }], // `transformResponse` 在傳遞給 then/catch 前,允許修改響應數據 transformResponse: [function (data) { // 對 data 進行任意轉換處理 return JSON.parse(data); }] })
添加請求和響應攔截器
給實例添加請求和響應攔截器,根據實際數據格式進行相應的處理。
比如:
請求時,應后端要求根據 Content-type 設置 data 傳參格式;
響應時,統一處理登錄超時的情況和請求失敗的錯誤提示處理等。
//request.js // 實例添加請求攔截器 instance.interceptors.request.use(function (config) { // 在發送請求之前做處理... config.headers = Object.assign(config.method === 'get' ? { 'Accept': 'application/json', 'Content-Type': 'application/json; charset=UTF-8' } : { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, config.headers); if (config.method === 'post') { const contentType = config.headers['Content-Type']; // 根據Content-Type轉換data格式 if (contentType) { if (contentType.includes('multipart')) { // 類型 'multipart/form-data;' // config.data = data; } else if (contentType.includes('json')) { // 類型 'application/json;' // 服務器收到的raw body(原始數據) "{name:"nowThen",age:"18"}"(普通字符串) config.data = JSON.stringify(config.data); } else { // 類型 'application/x-www-form-urlencoded;' // 服務器收到的raw body(原始數據) name=nowThen&age=18 config.data = Qs.stringify(config.data); } } } return Promise.resolve(config); }, function (error) { // 對請求錯誤做處理... return Promise.reject(error); }); // 實例添加響應攔截器 instance.interceptors.response.use(function (response) { // 對響應數據做處理,以下根據實際數據結構改動!!... // const { reason_code } = response.data || {}; // if (reason_code === '001') { // 請求超時,跳轉登錄頁 // const instance = Toast('請求超時,即將跳轉到登錄頁面...'); // setTimeout(() => { // instance.close(); // window.location.href = '/login'; // }, 3000) // } return Promise.resolve(checkStatus(response)); }, function (error) { // 對響應錯誤做處理... return Promise.reject(checkStatus(error.response)); });
處理錯誤狀態:
//utils.js export function checkStatus (response) { const status = response.status || -1000; // -1000 自己定義,連接錯誤的status if ((status >= 200 && status < 300) || status === 304) { // 如果http狀態碼正常,則直接返回數據 return response.data; } else { let errorInfo = ''; switch (status) { case -1: errorInfo = '遠程服務響應失敗,請稍后重試'; break; case 400: errorInfo = '400:錯誤請求'; break; case 401: errorInfo = '401:訪問令牌無效或已過期'; break; case 403: errorInfo = '403:拒絕訪問'; break; case 404: errorInfo = '404:資源不存在'; break; case 405: errorInfo = '405:請求方法未允許' break; case 408: errorInfo = '408:請求超時' break; case 500: errorInfo = '500:訪問服務失敗'; break; case 501: errorInfo = '501:未實現'; break; case 502: errorInfo = '502:無效網關'; break; case 503: errorInfo = '503:服務不可用' break; default: errorInfo = `連接錯誤` } return { status, msg: errorInfo } } }
封裝實例
實例封裝到 async\await 異步函數中。
//request.js const request = async function (opt) { try { const options = Object.assign({ method: 'get', ifHandleError: true // 是否統一處理接口失敗(提示) }, opt); // 匹配接口前綴 開發環境則通過proxy配置轉發請求; 生產環境根據實際配置 options.baseURL = autoMatch(options.prefix); const res = await instance(options); // console.log(res); if (!opt.ifHandleError) { // 自定義參數,是否允許全局提示錯誤信息 Toast(res.error || '請求處理失敗!') } return res; } catch (err) { if (!opt.ifHandleError) { // 自定義參數,是否允許全局提示錯誤信息 Toast(err.msg || '請求處理失敗!') } return err; } }
- 此處可以增加對請求入參進行合並或默認值處理;
- 自定義參數配置
項目中統一處理報錯提示。但有時對於接口請求比較多的情況,很多時候並不希望某些接口被全局報錯提示。這時這種接口可以設置自定義的 ifHandleError 參數處理,不進行全局錯誤提示,而是到業務代碼中再做處理。
同理,其他的一些特殊業務情況也可以通過自定義參數處理。
- 根據自定義的 prefix 參數匹配接口前綴;
對於項目中請求多個后端的接口時,每個接口在請求封裝時附帶 prefix 參數,根據 prefix 的值 autoMatch函數
統一處理,自動匹配接口前綴。
- 開發環境可通過 webpack proxy 配置轉發請求。
- 生產環境讀取實際配置(根據實際項目的情況)。
autoMath 方法:
// utils.js const isDev = process.env.NODE_ENV === 'development'; // 開發 or 生產 // 匹配接口前綴 export function autoMatch (prefix) { let baseUrl = ''; if (isDev) { // 開發環境 通過proxy配置轉發請求; baseUrl = `/${prefix || 'default'}`; } else { // 生產環境 根據實際配置 根據 prefix 匹配url; // 配置來源 根據實際應用場景更改配置。(1.從全局讀取;2.線上配置中心讀取) switch (prefix) { case 'baidu': baseUrl = window.LOCAL_CONFIG.baidu; break; case 'alipay': baseUrl = window.LOCAL_CONFIG.alipay; break; default: baseUrl = window.LOCAL_CONFIG.default; } } return baseUrl; }
開發環境接口代理
webpack 配置,比如:@vue/CLI3 在 vue.config.js
中配置:
devServer: { proxy: { '/baidu': { target: 'http://10.59.81.31:8088', changeOrigin: true, pathRewrite: { '^/baidu': '' } }, '/default': { target: 'http://10.59.81.31:8088', changeOrigin: true, pathRewrite: {'^/default' : ''} }, } },
統一配置接口
在一個新文件 apiUrl.js
中統一管理項目全部的接口,方便維護。
//apiUrl.js export const apiUrl = { login: '/api/login', loginOut: '/api/loginOut', qryPageConfig: '/test/qryPageConfig', setPageConfig: '/test/setPageConfig', //... }
生成接口
在 index.js
中:
import Vue from 'vue'; import request from './request'; import { apiUrl } from './apiUrl'; let services = {}; Object.entries(apiUrl).forEach((item) => { services[item[0]] = function (options = {}) { return request(Object.assign({ url: item[1] }, options)) } }) // 將services掛載到vue的原型上 // 業務中引用的方法:this.$services.接口名(小駝峰) Object.defineProperty(Vue.prototype, '$services', { value: services }); export default services;
在上述代碼中,通過讀取配置的接口信息 (名稱和請求路徑),生成全部接口。 並掛載到 vue 的原型上,這樣在業務中就可以通過 this.$services.接口名
直接調用對應的接口。
當然,這是本項目使用的方法。 因具體項目而異,也可以不掛載到 Vue 原型上,使用 services 模塊;或直接調用 request.js
封裝的請求函數。
業務調用
以下為使用事例:
// index.vue methods中 methods: { // 獲取頁面配置信息接口 get Promise $_qryPageConfig() { this.$services.qryPageConfig({ method: 'get', //默認 params: { page: 'login' }, }).then((res) => { this.pageConfig = res.data; }).finally(() => { ... this.close(); }); }, // 設置頁面配置接口 post async/await $_order: async function () { this.loading = true; const res = await this.$services.setPageConfig({ prefix: 'baidu', //匹配url前綴 ifHandleError: true, //不對該接口進行全局錯誤提示。 method: 'post', headers: { 'Content-Type': 'application/json; charset=UTF-8', }, data: { userId: this.idCard, userName: this.realName, color: 'red', } }) this.loading = false; if (res.data) { Total('success'); } }, }
項目代碼
項目代碼本身依附於 @vue/CLI3 構建,是 CLI3 構建移動 H5 端應用的一部分。當然通過剝離 vue 代碼,該 Axios 封裝也可以直接應用到其他項目中。
項目源碼:
@vue/CLI3 構建移動 H5 端應用:juejin.im/post/5d674d…;
Github 鏈接:github.com/now1then/vu…;