Серия Millions of PV Mall Practice — Практика инкапсуляции запросов командного интерфейса

внешний интерфейс JavaScript React.js
Серия Millions of PV Mall Practice — Практика инкапсуляции запросов командного интерфейса

⚠️ Эта статья является первой подписанной статьей сообщества Nuggets, и ее перепечатка без разрешения запрещена.

Введение

Эта статья商城实践系列В третьей статье в основном представлены некоторые конфигурации запросов интерфейса во время разработки проекта, что удобно для команд, выполняющих некоторую бизнес-обработку во время разработки.

Много друзей в ежедневной разработке, будь то использование родногоajax``、``fetchНу, это все еще более популярноaxios,umi-requestДругие библиотеки инкапсуляции будут выполнять уровень бизнес-инкапсуляции для выполнения некоторой интерактивной обработки запросов, таких как: привязка токена, истечение срока действия входа, сообщение об ошибке запроса, повторная попытка интерфейса и другие общие операции.

Итак, как же инкапсулировать в проекте метод запроса интерфейса, что может уменьшить некоторое дублирование работы при соблюдении бизнес-требований?

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

Существует пять основных категорий этих общих пакетов и конфигураций, и я перечислил их все на рисунке ниже.

image.png

В этой статье в качестве основного выбора библиотеки запросов используется umi-request, который представляет собой расширенную библиотеку пакетов, основанную на методе запроса на выборку. Подробнее см.: umi-запрос

Инициализировать проект

Прежде чем начать, чтобы лучше построить нашу инкапсуляцию интерфейсных запросов, нам нужно сделать некоторую предварительную работу по установке соответствующей библиотеки инструментов. Затем я проведу вас шаг за шагом, чтобы инициализировать наш файл запроса и выполнить простую инициализацию конфигурации.

Во-первых, нам нужно установитьumi-request, кстати для инициализации проекта.

// #npm
npm install --save umi-request

// #yarn
yarn add umi-request

Затем создайте файл-оболочку, как я вutilsсозданный подrequest.tsкак пакетный файл.

image.png

Наконец, в файле инициализируйте текущий экземпляр запроса черезumi-requestсерединаextendМетод инициализирует конфигурацию запроса и возвращаетRequestMethodпример.

настроено здесь超时时间(timeout),错误处理函数(errorHandler), 请求前缀(prefix)и другие основные параметры.

import { extend } from 'umi-request';



/** 配置request请求时的默认参数 */

const request = extend({

  prefix: INTERFACE_URL,

  timeout: 3000,

  errorHandler

});

Когда мы инициализируем соответствующую конфигурацию, мы генерируем функцию запроса интерфейсаrequestпример. Далее все, что нам нужно сделать, это настроить соответствующий хук-перехватчик.

перехватчик

В главе об инициализации мы упомянули перехватчики, так что же такое перехватчики и что они могут делать? Возможно многие знакомые новички во фронтенд разработке будут "запутаны" Далее я расскажу о том, что из себя представляет механизм перехватчика и как настроить перехватчик в нашем проекте.

Во-первых, механизм перехватчика, как показано на следующем рисунке. мы можем пройти拦截器Механизм выполняет некоторую промежуточную обработку перед запросом и после ответа.

Во время этого процесса вы можете передать текущий экземпляр запросаrequestсерединаinterceptorsизAPI, чтобы быть текущим перехватчиком.

  • request.interceptors.request.use: перехватчик запросов
  • request.interceptors.response.use: перехватчик ответа

В перехватчике запросов текущий запрос может быть请求地址(url)а также请求配置(options)Дождитесь модификации, а затем запрос будет использовать новую конфигурацию запроса для выполнения запроса интерфейса.

// request拦截器, 改变url 或 options.
request.interceptors.request.use((
  url: string, 
  options: RequestOptionsInit
) => {
  return {
    url: `${url}&interceptors=yes`,
    options,
  }
})

В перехватчике ответа вы можетеresponseРезультат выполняет некоторую общую обработку, стоит отметить, что нам нужно использоватьresponse.clone().json()разбиратьresponseрезультат.

// 克隆响应对象做解析处理

request.interceptors.response.use(async (response: Response) => {

  const data = await response.clone().json();

  if (data.code === 500) {

      throw new Error('throw error ......')

  }

  return response;

});

Здесь дается только краткое введение в перехватчик, а функция перехватчика будет расширена в следующих главах для более глубокого использования инкапсуляции.

Код и бизнес

В контексте бизнеса наша (подтверждение) система заказов использует кодовые коды для описания соответствующих идентификаторов ошибок исключений заказа. Как показано на рисунке ниже, каждый кодовый код соответствует отклонению в подтверждении заказа. В соответствии с различными уровнями и серийными номерами , мы можем выполнять политику и обработку конечного автомата в соответствии со статусом кода, возвращаемым серверной частью на странице обработки.

В реальной разработке мы используем многоcodeВместо того, чтобы возвращать текущий статус запроса, то есть в развитии бизнеса,HTTP请求Состояние может быть описано по наследству. Итак, давайте рассмотрим некоторые маленькие хитрости кодовой системы.

Во-первых, давайте посмотрим на структуру ответа базового запроса RESTFul:

{
    code: 200,
    message: ""
}
{
    code: 201,
    message: ""
}
{
    code: 202,
    message: ""
}
{
    code: 203,
    message: ""
}

Мы переместили состояние запроса вresponseдля обработки. При нормальном доступе к интерфейсу интерфейсному интерфейсу нужно толькоcodeвыполнить операцию. Разберитесь со статусом.

Поэтому разделим код на следующие распространенные состояния ошибки:

Одним из них является статус нашего запроса интерфейса.Если этот вид оценки статуса является успешным статусом, он будет возвращен напрямую.responseсерединаdata.如果失败则直接进行错误处理。

Второй — нештатное состояние некоторых служб, в том числе системные ошибки, то есть ошибки кода. Далее следуют ошибки таких сторонних сервисов (таких как WeChat, DingTalk).

В-третьих, некоторая обратная логика в нашей разработке может быть согласована с пользовательскими кодами ошибок.

Ниже мы также перечисляем некоторые распространенные HTTP-коды в качестве идентификаторов, которых вполне достаточно в большинстве сценариев. Я не буду здесь подробно перечислять все публичные состояния.

Будь то серверная часть или интерфейсная часть, для этого есть несколько относительно полных библиотек.

export enum HTTP_STATUS {
  SUCCESS = 200,
  CREATED = 201,
  UPDATED = 202,
  DELETED = 203,
  CLIENT_ERROR = 400,
  AUTHENTICATE = 401,
  FORBIDDEN = 403,
  NOT_FOUND = 404,
  SERVER_ERROR = 500,
  BAD_GATEWAY = 502,
  SERVICE_UNAVAILABLE = 503,
  GATEWAY_TIMEOUT = 504
}

export enum CUSTOM_HTTP_CODE {
  MESSAGE_TOAST = 601,
  SERVICE_TRY_CATCH = 602
  ......
}

Когда у нас будет базовое определение кода, давайте приведем несколько реальных сценариев использования и посмотрим, как они используются.

Срок действия личности истек

Говоря об истечении срока действия личности, большинство студентов, возможно, думали об этом.401это государство. Большая часть бизнес-логики заключается в очистке кеша и повторном входе пользователя.

В сложных сценариях существует множество определений срока действия удостоверения, включая истечение срока действия токена, единый вход, исключение удостоверения и т. д. Поэтому мы будем вызывать соответствующий метод обработки в перехватчике следующим образом:

/**
 * [ u -> 401 ] 用户身份已经过期,清除后重新生成新的身份
 * [ u -> 402 ] 用户身份异常,清除后重新生成新的身份
 * [ u -> 403 ] 用户在其他地方登录,清除后重新生成新的身份
 * */
if (
  responseBody.code === 401 ||
  responseBody.code === 402 ||
  responseBody.code === 403
) {
  clearUserTraces();
  return undefined;
}

Судя по коду, мы инициируем выполнениеclearUserTracesчтобы направить пользователя к интерфейсу входа в систему для завершения нового авторизованного входа.

/**
 * 清除当前用户身份信息,并且跳转到登录页面
 */
 function clearUserTraces () {
  Modal.confirm({
    title: '提示信息',
    icon: createElement(ExclamationCircleOutlined),
    content: '您当前设备信息已过期,可以取消继续留在该页面,或者重新登录。',
    okText: "登录",
    cancelText: "关闭",
    onOk() {
      history.replace('/login')
    }
  })
}

image.png

Настройка уведомлений

В большинстве средних и фоновых проектов задействованы панели уведомлений разных типов и состояний. отant designСудя по стилю панели уведомлений, мы разделены наmessageа такжеnotificationДва стиля уведомлений и разныеstatusпоказывать.

На основе этой информации мы также согласовали поле с бэкендомshowMessageИспользуется для управления некоторыми сообщениями об ошибках. Давайте посмотрим на текущее определение интерфейса:

type CommonResult<T = any> = {
  data: T;
  code: string;
  showMessage:
    | undefined
    | false
    | {
        method: "message" | "notification";
        type: "success" | "error" | "info" | "warning";
        message: string;
        description?: string;
      };
};

Серверная часть может управлять подсказкой уведомления внешнего интерфейса в интерфейсе через возвращенную основную модель, которая в основном используется в бизнесе для предоставления соответствующих подсказок об успехе и неудаче операции.

Затем в перехватчике ответа я делаюshowMessageвынес приговор. следующим образом, еслиshowMessageвыполнить, если он существуетshowMessageNotificationHandlerметод обработки всплывающей информации.

if (responseBody.showMessage) {
  showMessageNotificationHandler(responseBody.showMessage)
}

showMessageNotificationHandlerЧто именно вы делаете? Давайте посмотрим на реализацию метода.Общая идея реализации заключается вswitch caseПоказывает различные всплывающие окна компонентов для типов и параметров, переданных из серверной части.

/**
 * 显示通知弹窗
 * @param showParams 显示通知参数
 * @param api 当前标识
 */
export function showMessageNotificationHandler(
  showParams: API.CommonResult["showMessage"],
  api: string = ""
): void {
  /** @name 判断是否开启了通知描述显示 */
  if (showParams) {
    const { method, type, message, description } = showParams;
    switch (method) {
      /** @name 全局提示弹窗 */
      case "message":
        showMessage[type](message);
        break;

      /** @name 通知提示弹窗 */
      case "notification":
        notification[type]({
          message,
          description,
        });
        break;
      default:
        throw new Error(
          `[ request ]: 接口约定了弹窗信息参数,但是未给出使用模型,请检测当前。${api}`
        );
    }
  }
}

После инкапсуляции мыMockИнтерфейс, чтобы увидеть эффект, мы возвращаемmessageВсплывающая информация, чтобы увидеть эффект:

'GET /api/service-admin/v1/data/table': {
  code: 40001,
  message: '操作成功',
  showMessage: {
    method: 'message',
    type: 'error',
    message: '我是一个自定义的message消息',
  },
  data: {}
}

image.png

обработка ошибок

При обработке ошибок мы будем обрабатывать некоторые ошибки запроса. за исключениемHTTPВ дополнение к соответствующей обработке ошибок исходного состояния также необходимо настроитьcodeэкспортировать, вернутьPromise.Rejectошибка вышла. мы можемtry catchПерехватите текущее исключение и займитесь деломcodeиметь дело с.

/** 异常处理程序 */
function errorHandler(
  error: ResponseError<any> & {
    code?: string | number;
  }
) {
  const { response, request } = error;
  if (response && response.status) {
    const errorText = codeMessage[response.status] || response.statusText;
    const { status, url } = response;

    notification.error({
      message: `请求错误 ${status}: ${url}`,
      description: errorText,
    });
  }

  if (error.code && error.code === 5000) {
    // todo: 上报出错人以及对应信息
    throw new Error(`[fetch fail]: 接口请求失败,当前接口地址${request.url}`);
  }

  return Promise.reject(error);
}

В следующем примере кода мы находимся на уровне вызоваtry catchПопробуйте захватить информацию о запросе текущего интерфейса, как показано ниже,40001изcodeНеобработанный в перехватчике ответа, таким образом перетекающий вerrorHandleЕсли он все еще не обработан, то он передается вызывающей стороне для обработки:

const handleRequest = async (
  props: API.TableRequestParams
): Promise<{
  data: API.GithubIssueItem[];
  success: boolean;
  total: number;
}> => {
  try {
    const res = await fetchTableList(props);
    return {
      data: res.list,
      success: true,
      total: res.total,
    };
  } catch (throwError: unknown) {
    console.log(throwError, "throwError");
    return {
      data: [],
      success: false,
      total: 0,
    };
  }
};

image.png

Отображение конфигурации

Когда у нас есть инкапсулированная функция запроса интерфейса, чтобы облегчить вызов членов команды, я также инкапсулировал сопоставление конфигурации.umiПлагины для обработки преобразования функций интерфейса в спецификации интерфейса.

Как показано на рисунке ниже, мы объявим карту конфигурации, а затем абстрагируем ее в функцию. Удобно звонить во время разработки.

image.png

Здесь я расскажу вам, как его использовать.

Во-первых, яservicesВ директории объявляется файл конфигурации, его стандартная форма — объект, также можно быть JSON-файлом. соглашениеKeyЗначением является имя функции сопоставления.Valueда请求方式а также请求地址.

Object写法

module.exports = {
  fetchTableList: 'GET /service-admin/v1/data/table'
}

JSON写法

{
  "fetchLogin": "POST /service-admin/v1/user/login",
  "fetchUserInfo": "POST /service-admin/v1/user/info"
}

Когда у нас есть файл конфигурации, соответствующая функция запроса будет сгенерирована при запуске проекта, как показано на следующем рисунке:

image.png

Плагин преобразует соответствующий файл конфигурации в функцию запроса интерфейса. Итак, как мне использовать функцию плагина для достижения этой функции? Давайте посмотрим на ядро ​​​​плагина

Мы конвертируем конфигурации интерфейса в разных директориях в соответствующие конфигурации, а затем выводимtemplateшаблон для завершения сопоставления.

Map(1) {
  '/Users/wangly19/Desktop/Project/plugins/example/./constant/a.ts' => [
    {
      functionName: 'a',
      url: '/app-services/${id}',
      method: 'post',
      linkParams: [Array],
      exportTemp: 'const { id, ...data } = payload',
      paramType: 'data'
    }
  ]
}

Выше приведена конфигурация, которую мы сгенерировали, давайте посмотрим на содержимое шаблона.

мы можем поставитьMapсредиValueОн преобразуется в следующий шаблон и, наконец, становится полным файлом функции интерфейса на выходе.

import request from '{{{ requestPath }}}'
import { RequestOptionsInit } from 'umi-request'


{{ #requestASTModules }}

export function {{ functionName }} <T = any, R = any>(
  payload?: T = {}, 
  options?: RequestOptionsInit = {},
): Promise<R> {

  {{#exportTemp}}
    {{{ exportTemp }}}
  {{/exportTemp}}
  {{^exportTemp}}
    /* [info]: @no link params */
  {{/exportTemp}}
  
  return request( `{{{ url }}}`, {
    {{ paramType }}: {{#exportTemp}}data{{/exportTemp}}{{^exportTemp}}payload{{/exportTemp}},
    method: '{{ method }}',
    ...options
  })
}

{{ /requestASTModules }}

Давайте посмотрим на эффект, как показано ниже:

Kapture 2021-08-25 at 22.27.15.gif

ресурс

Суммировать

В этой статье делается некоторая контрактная инкапсуляция для запросов в команде, а также базовая инкапсуляция бизнес-сценариев, которые могут встречаться в повседневной разработке, с разграничением ошибок интерфейса и серии успешных взаимодействий. 🤔 Объясняет, как управлять и контролировать гибкое состояние в командном бизнесе. В то же время, в последнем случае разделения в командной разработке сопоставление конфигурации управления запросами интерфейса инкапсулируется в подключаемый модуль, и членам команды не нужно заботиться об этом.requestРазнообразие методов запроса может сосредоточиться на развитии бизнеса и интерфейсных вызовах.

Наконец, мы также попробовали некоторые другие сценарии. Позже мы можем продолжить расширять и оптимизировать его.Я перечисляю эти пункты ниже:

  • Механизм повторной попытки запроса интерфейса, который можетrequestБизнес-логика для повторной попытки после сбоя
  • Когда страница уходит, запрос интерфейса автоматически останавливается, и вы можете передатьcancelTokenа такжеcontroller.abortотменить запрос интерфейса
  • Подключение к платформе управления интерфейсом, генерация конфигурации сопоставления интерфейса одним щелчком мыши, сокращение ручной работы
  • Подключите swagger для генерации некоторых типов запросов интерфейса typescript

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

Для проекта торгового центра задача заключается не в логике реализации функций, а в оптимизации части визуального опыта и впечатлений. Если вы считаете, что статья полезна для вас, вы можете нажать 👍 и подбодрить меня. Если вы хотите узнать больше о внешних проектах электронной коммерции, Yoyos может подписаться на эту колонку.

последние хорошие статьи

концевые сноски

Эта статья была впервые опубликована в: Nuggets Technology Community
Тип: Подписанная статья
Добавить Автора
Собрано в колонке: # Million PV Mall Practice Series
Публичный номер: Жизнь программы ItCodes