axios封裝


優點

  • 統一維護管理接口;
  • 支持接口代理轉發;
  • 讀取環境配置,區分處理環境。
  • 攔截請求和響應,處理登錄超時、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;
  }
}
  1. 此處可以增加對請求入參進行合並或默認值處理;
  2. 自定義參數配置

項目中統一處理報錯提示。但有時對於接口請求比較多的情況,很多時候並不希望某些接口被全局報錯提示。這時這種接口可以設置自定義的 ifHandleError 參數處理,不進行全局錯誤提示,而是到業務代碼中再做處理。
同理,其他的一些特殊業務情況也可以通過自定義參數處理。

  1. 根據自定義的 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…


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM