接口请求
项目中采用基于 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 专用客户端:
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 与业务侧租户头等);其他消费方若无此类需求,可在该函数内按需删减或修改,无需配置环境变量。
// 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;登出、刷新 Token 等)。 |
初始化参数说明
createHttpClient 的完整选项类型为 CreateHttpClientOptions(定义于 @uniboot/request)。
| 配置项 | 类型 | 说明 |
|---|---|---|
baseURL | string | 接口基础地址。开启代理 configs.proxy 时用 /api,否则用 configs.baseUrl。 |
timeout | number | 超时时间(毫秒),库内默认 60000。 |
withCredentials | boolean | 是否跨域携带 Cookie;authMethod 为 SESSION_COOKIE 时启用。 |
headers | Record<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,空值或抛错视为失败。 |
onRefreshTokenFail | (error: unknown) => void | 刷新失败回调;模板中清空登录态并跳转登录页。 |
extraRequestHook | (config) => config | 追加在默认请求拦截器之后,可改写单次请求配置。 |
overrideResponseHook / overrideErrorHook | 拦截器函数 | 完全覆盖默认响应 / 错误拦截(一般不推荐)。 |
defaultRequestOptions | Partial<RequestOptions> | 见下一节。 |
默认请求选项 (defaultRequestOptions)
单次调用可通过第二个参数覆盖这些选项。
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
isParamsToData | boolean | true | POST 且未设置 data 时,自动把 params 挪到 data。 |
isReturnDefaultResponse | boolean | false | 为 true 时返回完整 Axios 响应(含 headers、status),常用于 blob / 需读响应头的场景。 |
isTransformResponse | boolean | true | 为 true 时按约定解包业务体;为 false 时直接返回 response.data。 |
urlPrefix | string | configs.urlPrefix | 拼在 baseURL 之后、业务 url 之前的前缀。 |
ignoreCancelToken | boolean | false | 为 false 时启用重复请求取消:相同请求未完成前再次发起会取消上一次。 |
withToken | boolean | true | 是否自动携带 Token。刷新 Token、登录、验证码等接口应设为 false,避免 401 刷新递归。 |
errorTip | boolean | true | 失败时是否走 msgError 提示。 |
successTip | boolean | false | 成功且响应带 message 时是否走 msgSuccess(增删改类接口可开)。 |
isOpenRetry | boolean | true | 是否在超时 / 断网时自动重试。 |
retryCount | number | 2 | 最大重试次数。 |
重试范围
自动重试仅针对 非 POST 请求,且错误码为 ECONNABORTED(超时)或 ERR_NETWORK(断网)。POST 不会自动重试,避免重复提交。
Token 自动刷新(401)
当配置了 refreshToken 时,错误拦截器在满足以下条件时会尝试刷新并重试原请求:
- HTTP 状态码为 401
- 该请求的
withToken !== false - 该请求尚未因刷新而重试过(内部标记
_retryAfterRefresh)
行为要点:
- 并发合并:刷新进行中时,其它 401 请求会排队,共享同一次
refreshToken()结果,避免并发重复刷新。 - 成功:拿到新 access token 后,用原配置重发请求。
- 失败:调用
onRefreshTokenFail,并 reject 原始错误。 - 刷新接口自身:必须设置
withToken: false,否则可能再次触发刷新逻辑。
模板中 refreshToken 通过 refreshHttpAccessToken() 动态 import('@/stores/modules/user') 调用 refreshAccessToken();刷新接口与登录共用 authHttp(见 src/api/user.ts 的 refreshAccessToken),保证打到授权服务而非业务 API。
OAuth 换票成功后由权限守卫调用 userStore.setToken() 写入 access token;登出与刷新失败均调用 handleSessionExpired() 清空登录态。
响应解包与错误提示
默认响应拦截器(isTransformResponse: true)按 HTTP 状态处理:
成功(2xx)
- 若
isReturnDefaultResponse,直接返回完整AxiosResponse。 - 否则从响应体中取业务数据:优先
result,其次data,否则整份data。 - 若开启
successTip且存在message,调用msgSuccess(translate(message))。 - 返回解包后的业务数据。
失败(错误拦截器)
| 场景 | 提示策略 |
|---|---|
请求被取消(ERR_CANCELED) | 静默,不提示 |
| 401 | SESSION_TIMEOUT → 文案键 auth.loginTimeout;否则用服务端 message 或「未授权」 |
| 403 | 「权限不足」 |
| 4xx | 优先拼接 details(field: message),否则用服务端 message |
| 5xx | 「系统繁忙,请稍后重试」 |
| 超时 / 其它网络错误 | 「请求超时…」或原始 error.message |
errorTip: false 时可关闭上述气泡,由调用方自行处理。
接口调用示例
1. 标准业务 POST
import request from '@/utils/request'
export function createOrder(data: CreateOrderParams) {
return request.post<CreateOrderResult>({
url: '/order/create',
data,
})
}2. 覆盖提示与重试
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(auth 模板登录为 OAuth,无 authApi.login):
import { adminApi, authHttp } from '@/utils/request'
export function getUserInfo() {
return adminApi.getCurrentUser()
}
export function logout() {
return authHttp.get<unknown>({ url: '/oauth/logout' })
}4. 验证码等特殊响应(blob / 不解包)
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. 表单上传
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.ts | HTTP 共享配置、权限守卫、账户模块桥接 |
src/utils/request.ts | 模板侧客户端实例(httpCommonOptions、admin/auth 导出) |
src/utils/auth-tokens.ts | 登录 / 刷新响应 token 解析(snake_case / camelCase 兼容) |
src/utils/auth.ts | clearAuthInfo、handleSessionExpired |
@uniboot/request → createHttpClient.ts | 拦截器、401 刷新排队、响应解包与错误提示 |
@uniboot/request → axios.ts | 方法封装、重复请求取消、超时/断网重试 |
@uniboot/request → types.ts | RequestOptions / RequestData 类型定义 |