接口请求 ​

项目中采用基于 axios 深度封装的 @uniboot/request 库处理所有 HTTP 请求。模板在 src/utils/request.ts 中通过 createHttpClient 创建业务客户端,并与 @uniboot/admin / @uniboot/auth 的账户相关客户端共用同一套鉴权与提示配置(由 src/setup/auth-runtime.ts 的 buildHttpCommonOptions 统一生成),保证改密、拉用户信息等接口也能自动携带 Token。

认证相关的桥接逻辑(HTTP 刷新回调、权限守卫、账户模块初始化)集中在 src/setup/auth-runtime.ts;src/utils/request.ts 仅负责创建各 HTTP 客户端实例。


客户端初始化 ​

src/utils/request.ts 调用 buildHttpCommonOptions() 生成共享配置,再分别创建业务 request 与 admin/auth 专用客户端:

ts
import { createUnibootAdminHttpBundle } from '@uniboot/admin'
import { createHttpClient } from '@uniboot/request'

import configs from '@/config'
import { buildHttpCommonOptions } from '@/setup/auth-runtime'

export const httpCommonOptions = buildHttpCommonOptions()

const request = createHttpClient({
  baseURL: configs.proxy ? '/api' : configs.baseUrl,
  ...httpCommonOptions,
})

export const { adminApi, userApi, roleApi, authHttp, adminHttp } =
  createUnibootAdminHttpBundle({
    authServiceUrl: configs.authServiceUrl,
    adminServiceUrl: configs.adminServiceUrl,
    http: httpCommonOptions,
  })

export default request

公共请求头在 src/config/index.ts 的 buildHttpHeaders 中写死默认值(含 version 与业务侧租户头等);其他消费方若无此类需求,可在该函数内按需删减或修改,无需配置环境变量。

ts
// src/setup/auth-runtime.ts(节选)
export function buildHttpCommonOptions() {
  return {
    headers: config.httpHeaders,
    getToken,
    refreshToken: refreshHttpAccessToken, // 动态 import user store
    onRefreshTokenFail: onHttpRefreshTokenFail, // 调用 handleSessionExpired()
    // ...NProgress、i18n 提示等
  }
}

导出说明 ​

导出项用途
request(默认导出)业务接口通用客户端(APP_PUBLIC_API_URL),src/api 中大部分自定义接口使用它。
httpCommonOptions与 admin/auth 客户端共享的超时、Token、提示、刷新等配置。
adminApi / adminHttp用户服务账号域 API / 底层 HTTP(改密、当前用户、验证码等),均走 APP_PUBLIC_ADMIN_SERVICE_URL。
userApi / roleApi用户 / 角色管理域 API,固定绑定 adminHttp。
authHttp授权服务底层 HTTP(APP_PUBLIC_AUTH_SERVICE_URL;登出等)。换票 / 刷新是否用它取决于网关是否聚合 /oauth/*,见下文。

初始化参数说明 ​

createHttpClient 的完整选项类型为 CreateHttpClientOptions(定义于 @uniboot/request)。

配置项类型说明
baseURLstring接口基础地址。开启代理 configs.proxy 时用 /api,否则用 configs.baseUrl。
timeoutnumber超时时间(毫秒),库内默认 60000。
withCredentialsboolean是否跨域携带 Cookie;authMethod 为 SESSION_COOKIE 时启用。
headersRecord<string, string>全局公共请求头;默认会合并 Content-Type: application/json。
getToken() => string | null | undefined读取当前 access token,供拦截器写入鉴权头。
buildAuthHeader(token: string) => Record<string, string>自定义鉴权头格式,默认 Authorization: Bearer ${token}。
onRequestStart / onRequestEnd() => void请求开始 / 结束钩子;模板中用于 NProgress。
translate(msg: string) => string将后端 message / 内置文案键转为展示文案(如 i18n)。
msgSuccess / msgError(msg: string) => void成功 / 失败全局提示。
refreshToken() => Promise<string | null | undefined>收到 401 时刷新 access token;返回新 token,空值或抛错视为失败。多个 client 应传同一函数引用以便跨实例合并。
onRefreshTokenFail(error: unknown) => void刷新失败回调;模板中清空登录态并跳转登录页。
extraRequestHook(config) => config追加在默认请求拦截器之后,可改写单次请求配置。
overrideResponseHook / overrideErrorHook拦截器函数完全覆盖默认响应 / 错误拦截(一般不推荐)。
defaultRequestOptionsPartial<RequestOptions>见下一节。

默认请求选项 (defaultRequestOptions) ​

单次调用可通过第二个参数覆盖这些选项。

选项类型默认值说明
isParamsToDatabooleantruePOST 且未设置 data 时,自动把 params 挪到 data。
isReturnDefaultResponsebooleanfalse为 true 时返回完整 Axios 响应(含 headers、status),常用于 blob / 需读响应头的场景。
isTransformResponsebooleantrue为 true 时按约定解包业务体;为 false 时直接返回 response.data。
urlPrefixstringconfigs.urlPrefix拼在 baseURL 之后、业务 url 之前的前缀。
ignoreCancelTokenbooleanfalse为 false 时启用重复请求取消:相同请求未完成前再次发起会取消上一次。
withTokenbooleantrue是否自动携带 Token。刷新 Token、登录、验证码等接口应设为 false,避免 401 刷新递归。
errorTipbooleantrue失败时是否走 msgError 提示。
successTipbooleanfalse成功且响应带 message 时是否走 msgSuccess(增删改类接口可开)。
isOpenRetrybooleantrue是否在超时 / 断网时自动重试。
retryCountnumber2最大重试次数。

重试范围

自动重试仅针对 非 POST 请求,且错误码为 ECONNABORTED(超时)或 ERR_NETWORK(断网)。POST 不会自动重试,避免重复提交。


Token 自动刷新(401) ​

当配置了 refreshToken 时,错误拦截器在满足以下条件时会尝试刷新并重试原请求:

  1. HTTP 状态码为 401
  2. 该请求的 withToken !== false
  3. 该请求尚未因刷新而重试过(内部标记 _retryAfterRefresh)

行为要点:

  • 并发合并(含多客户端):同一 refreshToken 函数引用在并发 401、以及多个 createHttpClient 实例(业务 / authHttp / adminHttp)之间共享同一次刷新,避免三路同时刷 token。
  • 成功:拿到新 access token 后,用原配置重发请求。
  • 失败:调用 onRefreshTokenFail(同一轮刷新只回调一次),并 reject 原始错误。
  • 刷新接口自身:必须设置 withToken: false,否则可能再次触发刷新逻辑。

模板中:

  1. buildHttpCommonOptions() 把同一个 refreshHttpAccessToken 传给业务 request 与 admin Bundle。
  2. refreshHttpAccessToken 调用 @uniboot/auth 的 oauthAuthService.refreshAccessToken(data: { refreshToken },并关闭 transform / errorTip)。
  3. OAuth 的 http 通常绑业务 request(网关聚合 /oauth/*);登出则用 authHttp。详见 授权服务。

OAuth 换票成功后由权限守卫写入 access / refresh(及可选 expiresIn);登出与刷新失败均调用 handleSessionExpired() 清空登录态。


响应解包与错误提示 ​

默认响应拦截器(isTransformResponse: true)按 HTTP 状态处理:

成功(2xx)

  1. 若 isReturnDefaultResponse,直接返回完整 AxiosResponse。
  2. 否则从响应体中取业务数据:优先 result,其次 data,否则整份 data。
  3. 若开启 successTip 且存在 message,调用 msgSuccess(translate(message))。
  4. 返回解包后的业务数据。

失败(错误拦截器)

场景提示策略
请求被取消(ERR_CANCELED)静默,不提示
401SESSION_TIMEOUT → 文案键 auth.loginTimeout;否则用服务端 message 或「未授权」
403「权限不足」
4xx优先拼接 details(field: message),否则用服务端 message
5xx「系统繁忙,请稍后重试」
超时 / 其它网络错误「请求超时…」或原始 error.message

errorTip: false 时可关闭上述气泡,由调用方自行处理。


接口调用示例 ​

1. 标准业务 POST ​

ts
import request from '@/utils/request'

export function createOrder(data: CreateOrderParams) {
  return request.post<CreateOrderResult>({
    url: '/order/create',
    data,
  })
}

2. 覆盖提示与重试 ​

ts
import request from '@/utils/request'

export function deleteUser(id: number) {
  return request.post(
    {
      url: '/user/delete',
      data: { id },
    },
    {
      successTip: true,
      isOpenRetry: false,
    }
  )
}

3. 使用 admin / auth 客户端 ​

账户相关能力优先走 adminApi;登出走 authHttp(或 createAuthApi):

ts
import { createAuthApi } from '@uniboot/auth'
import { adminApi, authHttp } from '@/utils/request'
import { getToken } from '@/utils/auth'

export function getUserInfo() {
  return adminApi.getCurrentUser()
}

const authApi = createAuthApi({
  http: authHttp,
  logoutHttp: authHttp,
  getToken,
})

export function logout() {
  // 内部:withToken:false + 可选手动 Bearer,避免过期时误触发 refresh
  return authApi.logout()
}

4. 验证码等特殊响应(blob / 不解包) ​

ts
import { adminHttp } from '@/utils/request'

export function captcha(params: Record<string, unknown>) {
  return adminHttp.get(
    { responseType: 'blob', url: '/captcha', params },
    {
      isReturnDefaultResponse: true,
      isTransformResponse: false,
      withToken: false,
    }
  )
}

5. 表单上传 ​

ts
import request from '@/utils/request'

export function uploadAvatar(file: File) {
  const formData = new FormData()
  formData.append('file', file)

  return request.post({
    url: '/user/upload-avatar',
    data: formData,
    headers: {
      'Content-Type': 'multipart/form-data',
    },
  })
}

相关源码 ​

路径说明
src/setup/auth-runtime.tsHTTP 共享配置、权限守卫、账户模块桥接
src/utils/request.ts模板侧客户端实例(httpCommonOptions、admin/auth 导出)
src/utils/auth-tokens.ts登录 / 刷新响应 token 解析(snake_case / camelCase 兼容)
src/utils/auth.tsclearAuthInfo、handleSessionExpired
@uniboot/request → createHttpClient.ts拦截器、401 跨实例刷新合并、响应解包与错误提示
@uniboot/request → axios.ts方法封装、重复请求取消、超时/断网重试
@uniboot/request → types.tsRequestOptions / RequestData 类型定义