Skip to content

http-data-request

基于 axios 二次封装,可灵活适配的网络数据请求库

GitHub Repo stars GitHub forks

项目状态

CircleCI code coverage npm version license JavaScript Style Guide

版本更新内容请访问 Changelog

功能特性

  • 自动化保存与应用身份令牌
  • 统一处理异常信息
  • 可自定义状态码
  • 可自定义身份授权数据节点
  • 提供各请求方法的快捷函数
  • 提供取消当前所有请求函数

设计背景

http-data-request 适用于网页与服务器交互应用自定义编码体系,而不直接使用原生 http 状态码方案的场景

业务数据格式

服务器对于系统内业务请求均响应 http 200 状态,响应数据格式如下

json
{
  // 业务状态码
  "code": 0,
  // 业务异常信息,非异常情况仅输出 "ok"
  "msg": "ok",
  // 业务数据
  "data": {}
}

业务状态码 code 通常有以下几种情况

  • 0 请求成功
  • 10 access token 失效
  • 11 refresh token 失效
  • 其他业务异常

身份认证模式

插件提供了两种身份认证模式,两者的区别在于身份令牌失效的处理不同

认证成功处理流程

login-success

流程中 将令牌存储本地存储携带令牌发起请求 均为插件自动处理的行为

认证失败处理流程

双 token 模式

应用 access token 短时效,refresh token 长时效

  • 携带 access token 执行数据请求
  • 响应 access token 失效状态 { code: 10, ... }
  • http-data-request 自动使用 refresh token 发送刷新 access token 请求
  • 响应成功则更新 token 相关信息,并重新发起数据请求
  • 响应失败则认为用户身份认证已失效,需重新登录 { code: 11, ... }

refresh-token-invalid

单 token 模式

应用 access token 长时效模式

  • 携带 access token 执行数据请求
  • 响应 refresh token 失效状态 { code: 11, ... }
  • 失败则认为用户身份认证已失效,需重新登录

access-token-invalid

安装

http-data-request 组件安装到项目中

sh
npm i http-data-request
sh
yarn add http-data-request
sh
pnpm add http-data-request

在项目中添加一个文件,例如 /src/config/http/index.js,用于设置 http-data-request 的全局配置并导出各功能函数

ts
import { useHttpDataRequest } from 'http-data-request'
import type { HttpDataRequestOptions } from 'http-data-request'

const options: HttpDataRequestOptions = {
  baseUrl: 'https://example.com/api',
}

export const {
  http, get, post, put, patch, del, cancel
} = useHttpDataRequest(options)

在 vite 配置 vite.config.js 中,为 http 的项目安装模块目录设置别名

js
import { fileURLToPath, URL } from 'node:url'
import { resolve } from 'path'
import { defineConfig } from 'vite'

export default defineConfig({
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
      '@api': fileURLToPath(new URL('./src/api', import.meta.url)),
      '@http': fileURLToPath(new URL('./src/config/http', import.meta.url))
    }
  },
  ...
})

应用以上配置后,在项目的任意位置,都可以直接使用 @http 别名来引用 http 相关的函数

基础设定

快捷函数

useHttpDataRequest 创建的函数集中,http 函数是数据请求的核心函数,而 getpostputpatchdel 则是各请求方法的快捷函数

js
get('/user/1')
// 等价于
http('/user/1', undefined, { method: 'GET' })

http 默认的请求方法是 POST

url

提供的 url 若是一个相对路径,将会自动应用 baseUrl 进行拼接

js
const options: HttpDataRequestOptions = {
  baseUrl: 'https://example.com/api',
}

get('/user/1')

此处实际请求了 https://example.com/api/user/1 地址

js
get('https://other-example.com/api/user/1')

而 url 以 http/https 开头,将会直接使用该地址,不再拼接 baseUrl 内容

该设定提供了在系统内快速应用基础地址的编码简洁性,又保留了直接访问其他网站地址的灵活性

不匹配的响应格式

使用 http-data-request 请求数据成功,响应格式如下

ts
interface HttpDataRequestBody{
  code: number
  msg: string
  data: any
}

若响应数据格式与 HttpDataRequestBody 不匹配,将直接返回该数据,但与此同时全局异常处理在当次请求中将不会生效

应用实例

项目 API 定义

js
// src/api/user.js
import { get, post } from '@http'

export function getUser (userId) {
  return get(`/user/${userId}`)
}

应用于组件/页面中

vue
<template>
  <div>
    <div>
      User name: {{ user.name }}
    </div>
    <div>
      User age: {{ user.age }}
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue'
import { getUser } from '@api/user'

const user = ref({})

getUser(10).then(data => { user.value = data })
</script>

取消请求

取消当前正在执行的所有请求

vue
<template>
  <div>
    <div>
      ...order form
    </div>
    <button
      type="button"
      @click="save"
    >Save order</button>
    <!--
    Clicking before the data is saved successfully
    will cancel the data request
    -->
    <button
      type="button"
      @click="cancel"
    >Cancel</button>
  </div>
</template>
<script setup>
import { ref } from 'vue'
import { DialogMessageSuccess } from 'v-dialogs'
import { cancel } from '@http'
import { saveOrder } from '@api/order'

function save () {
  saveOrder({ ...formData }).then(() => {
    DialogMessageSuccess('Order saved successfully')
  })
}
</script>

全局配置

这里使用了 v-dialogs 组件作为示例,用于在对话框中显示异常信息内容

ts
import {
  useHttpDataRequest,
  EXCEPTION_BUSINESS,
  EXCEPTION_AUTH_INVALID
} from 'http-data-request'
import type { HttpDataRequestOptions } from 'http-data-request'
import { DialogMessageError, DialogAlertError } from 'v-dialogs'
import { logout } from './auth'

const options: HttpDataRequestOptions = {
  baseUrl: 'https://example.com/api',
  // global exception handle
  exception (message, type) {
    // display business exceptions message
    if (type === EXCEPTION_BUSINESS) {
      DialogMessageError(message)
      return
    }
    // display rest of other exceptions message
    DialogAlertError(message)
    // some action for user authorization expired
    if (type === EXCEPTION_AUTH_INVALID) {
      // cancel all current request when
      cancel()
      // logout and redirect to login page
      logout()
    }
  }
}

export const {
  http, get, post, put, patch, del, cancel
} = useHttpDataRequest(options)

上面的代码是全局异常处理在项目中的典型应用,其中有三部分处理内容

  • 静默式展示业务异常信息
  • 模态提示其他异常信息,该提示需要用户确认后才会继续,例如系统底层异常、网络异常、身份认证失效等
  • 当身份令牌失效时,取消所有当前请求并退出登录

设置了全局异常处理后,各类型异常信息将会自动展示,则业务代码中无需处理异常信息,仅需专注于处理业务逻辑即可

应用环境变量

baseUrl 选项指定了网络请求的基础网址前缀,在实际的项目应用中,需要针对不同的工作环境设置不同的地址

vite 工具链为例,在项目中创建以下三个 .env 环境配置文件

bash
# .env.development
VITE_BASE_URL=https://exapmle-dev.com/api

# .env.alpha
VITE_BASE_URL=https://exapmle-test.com/api

# .evn.production
VITE_BASE_URL=https://exapmle.com/api

设置了环境变量配置后,通过 import.meta.env.VITE_SOME_KEY 访问变量内容

ts
const options: HttpDataRequestOptions = {
  baseUrl: import.meta.env.VITE_BASE_URL
}

这样在不同环境的开发或构建时,会自动应用不同环境指定的基础网络地址

请求头身份令牌

在登录成功后,后续的数据请求会在请求头中添加身份令牌,默认设置下会添加以下字段

js
{
  'Authorization': 'Bearer access-token-value'
}

自定义属性名以及不添加 Bearer 前缀的设置

ts
const options: HttpDataRequestOptions = {
  tokenPrefix: false,
  keys: {
    header: 'x-http-request-access-token'
  }
}

应用该设置,请求头携带的 token 属性与值如下

js
{
  'x-http-request-access-token': 'access-token-value'
}

响应状态码

假设服务器响应的状态码方案如下

json
// 成功
{ "code": 1000, "msg": "ok", "data": { ... } }
// access token 失效
{ "code": 1100, "msg": "access token invalid", "data": {} }
// refresh token 失效
{ "code": 1200, "msg": "refresh token invalid", "data": {} }

根据服务器响应的状态码,设置 statuses 选项中的自定义状态码项目如下

ts
const options: HttpDataRequestOptions = {
  baseUrl: 'https://example.com/api',
  statuses: {
    success: 1000,
    invalidAccessToken: 1100,
    invalidRefreshToken: 1200
  }
}

如此 http-data-request 则一一对应了服务器响应的各种场景状态码

认证授权数据节点

http-data-request 会自动抓取身份令牌并将数据保存至 LocalStorage 中,插件默认匹配的数据格式如下

js
{
  access: {
    accessToken: 'access-token-value',
    refreshToken: 'refresh-token-value',
    expiresIn: 10086
  }
}

插件在处理数据请求返回结果时,始终会检测是否存在 access 数据节点,存在则自动进行存入或替换。这一机制无需手动处理 token 信息保存至本地存储的操作,统一 token 信息本地存储的位置与行为

也可以利用该机制实现自动登录的功能

ts
const options: HttpDataRequestOptions = {
  keys: {
    dataSet: 'accessData',
    accessToken: 'accessTokenKey',
    refreshToken: 'refreshTokenKey',
    expiresIn: 'expiresOn'
  }
}

该自定义节点设置,匹配了以下服务器的响应数据格式

js
{
  accessData: {
    accessTokenKey: 'access-token-value',
    refreshTokenKey: 'refresh-token-value',
    expiresOn: 10086
  }
}

当 access token 失效时,插件会自动发起刷新 token 的请求(请求的位置由 refreshUrl 选项指定),该请求的数据体如下

js
{
  refreshToken: 'refresh-token-value'
}

需要自定义请求体的字段名,可通过 keys.paramRefreshToken 选项进行设置

ts
const options: HttpDataRequestOptions = {
  keys: {
    paramRefreshToken: 'refreshTokenValue'
  }
}

应用该设置后,刷新 access token 时发送的请求体如下

js
{
  refreshTokenValue: 'refresh-token-value'
}

API

http-data-request 项目安装函数

ts
function useHttpDataRequest(
  options: HttpDataRequestOptions
): HttpDataRequestMethods

useHttpDataRequest 输入参数 HttpDataRequestOptions 的类型描述

ts
interface HttpDataRequestOptions {
  /**
   * 插件语言
   * @default `en`
   */
  language?: 'en' | 'zh-chs'
  /**
   * 基础请求路径
   * @default `/`
   */
  baseUrl: string
  /**
   * 执行刷新 access token 时的请求路径
   * @default `/auth/refresh-token`
   */
  refreshUrl?: string
  /**
   * @default 0
   */
  expiresIn?: number
  /**
   * 指定请求超时的时间,单位为毫秒
   * @default 10000
   */
  timeout?: number
  /**
   * 请求头携带的 token 是否添加 `Bearer` 前缀
   * @default true
   */
  tokenPrefix?: boolean
  /** 自定义数据节点名 */
  keys?: {
    /**
     * 授权相关数据的总节点名
     * @default `access`
     */
    dataSet?: string
    /**
     * access token 身份令牌
     * @default `accessToken`
     */
    accessToken?: string
    /**
     * refresh token 用于刷新 access token 令牌
     * @default `refreshToken`
     */
    refreshToken?: string
    /**
     * 令牌失效时间
     * @default `expiresIn`
     */
    expiresIn?: string
    /**
     * 发起刷新 access token 令牌时,请求体中携带 refresh token 的属性名
     * @default `refreshToken`
     */
    paramRefreshToken?: string
    /**
     * 请求头中用于携带 access token 令牌的属性名
     * @default `Authorization`
     */
    header?: string
  }
  /** 自定义响应状态码 */
  statuses?: {
    /**
     * 请求成功
     * @default 0
     */
    success?: number
    /**
     * access token 失效
     * @default 10
     */
    invalidAccessToken?: number
    /**
     * refresh token 失效
     * @default 11
     */
    invalidRefreshToken?: number
  }
  /** 请求异常全局处理 */
  exception?: (message: string, type: EXCEPTION_TYPE) => void
}

type EXCEPTION_TYPE =
  | 'exception-business'
  | 'exception-auth-invalid'
  | 'exception-system'

useHttpDataRequest 执行后创建的函数集 HttpDataRequestMethods 的类型描述

ts
interface HttpDataRequestMethods {
  /**
   * http 数据请求核心函数
   */
  http: typeof HttpDataRequest
  /**
   * http 请求应用 get、post、put、patch 与 delete 请求方法的快捷函数
   */
  get: typeof HttpDataRequest
  post: typeof HttpDataRequest
  put: typeof HttpDataRequest
  patch: typeof HttpDataRequest
  del: typeof HttpDataRequest
  /**
   * 取消所有当前的数据请求
   */
  cancel: () => void
}

function HttpDataRequest(
  // 数据请求地址
  url: string,
  // 请求数据
  data: any,
  // axios 原生请求配置参数
  options: AxiosRequestConfig
): Promise<any>

完整的 axios 配置参数请查看 axios-request-config

Released under the MIT License.