Оболочка Vue Axios

axios
Оболочка Vue Axios

предисловие

Сегодня в проектах библиотека Axios в основном используется для запросов интерфейса Http. Это http-библиотека на основе обещаний, которая работает на стороне браузера и в node.js. Кроме того, есть отличные функции, такие как перехват запросов и ответов, преобразование данных JSON и защита на стороне клиента от XSRF.axiosисходный код

"Связь-Axios Китайская документация"

Учитывая, что фактическое использование каждого проекта сбивает с толку и непоследовательно. Обобщенная инкапсуляция Axios помогает упростить код и облегчить последующее обновление и обслуживание, а также попытаться максимально обобщить его.

инструкция проекта

Сам код проекта прикреплен к сборке @vue/CLI3.Конечно, удалив код vue, пакет Axios также можно напрямую применить к другим проектам.

Цель пакета для достижения:

  • Единый интерфейс обслуживания и управления;
  • Поддержка переадресации прокси интерфейса;
  • Прочтите конфигурацию среды, чтобы отличить среду обработки.
  • Перехватывать запросы и ответы и обрабатывать нестандартные ситуации, такие как тайм-аут входа в систему и 404;
  • Сопоставьте префикс URL-адреса интерфейса в соответствии с запрошенной конфигурацией и поддержите его специальной обработкой.

упаковка

установить аксиомы

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 в соответствии с запросом бэкэнда;
При ответе унифицированная обработка времени ожидания входа в систему и обработка сообщений об ошибках при сбое запроса и т. Д.

//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) {
  // 对响应错误做处理...
    if (error.response) {
      return Promise.reject(checkStatus(error.response));
    } else if (
      error.code == "ECONNABORTED" &&
      error.message.indexOf("timeout") != -1
    ) {
      return Promise.reject({ msg: "请求超时" });
    } else {
      return Promise.reject({});
    }
});

Обработка состояний ошибки:

//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 (!res.success && options.ifHandleError) {
    // 自定义参数,是否允许全局提示错误信息
      Toast(res.error || '请求处理失败!')
    }
    return res;
  } catch (err) {
    if (options.ifHandleError) { // 自定义参数,是否允许全局提示错误信息
      Toast(err.msg || '请求处理失败!')
    }
    return err;
  }
}
  1. Здесь вы можете добавить обработку слияния или значений по умолчанию для входных параметров запроса;
  2. Конфигурация пользовательских параметров

Унифицированная обработка сообщений об ошибках в проекте. Но иногда для случая, когда есть много запросов интерфейса, во многих случаях ожидается, что некоторые интерфейсы не будут запрашиваться глобальной ошибкой. В настоящее время этот интерфейс может установить настраиваемый параметр ifHandleError для обработки вместо глобальной подсказки об ошибке, но для выполнения обработки в бизнес-коде.
Точно так же некоторые другие особые бизнес-ситуации также могут быть обработаны с помощью пользовательских параметров.

  1. Сопоставьте префикс интерфейса в соответствии с пользовательским параметром префикса;

Для интерфейсов, которые запрашивают несколько бэкэндов в проекте, каждый интерфейс сопровождается параметром префикса при запросе инкапсуляции в соответствии со значением префиксаautoMatch函数Унифицированная обработка и автоматическое сопоставление префиксов интерфейса.

  • Среда разработки может перенаправлять запросы через конфигурацию прокси-сервера webpack.
  • Рабочая среда считывает фактическую конфигурацию (в соответствии с фактическим проектом).

метод автоматематики:

// 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;
}

Прокси интерфейса среды разработки

конфигурация веб-пакета, например: @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, использовать модуль сервисов или вызывать его напрямую.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:nuggets.capable/post/684490…;

Ссылка на гитхаб:GitHub.com/now1 слишком жесток/ву…;