跳到主要内容

HTTP 与 API 客户端

HttpClient

基于 Axios 构建的底层 HTTP 客户端,负责处理:

  • 自动注入 Token
  • 401 响应时自动刷新 Token
  • 业务错误码检测
  • 文件上传与下载
  • 路径参数替换

创建 HttpClient

import { createHttpClient } from "@vef-framework-react/core";

const http = createHttpClient({
baseUrl: "/api",
timeout: 30_000,
okCode: 0,
tokenExpiredCode: 1002,
getAuthTokens: () => tokenStore.getTokens(),
setAuthTokens: tokens => tokenStore.setTokens(tokens),
refreshToken: async tokens => {
const result = await http.post("/auth/refresh", { data: tokens });
return result.data;
},
onUnauthenticated: () => router.navigate({ to: "/login" }),
showErrorMessage: message => notification.error(message)
});

HttpClientOptions

选项类型说明
baseUrlstring所有请求的基础 URL
timeoutnumber请求超时时间(毫秒)
okCodeMaybeArray<number>业务成功码
tokenExpiredCodeMaybeArray<number>Token 过期码
getAuthTokens() => Awaitable<AuthTokens | undefined>获取当前 Token
setAuthTokens(tokens) => Awaitable<void>持久化刷新后的 Token
refreshToken(tokens) => Awaitable<AuthTokens>Token 刷新回调
onUnauthenticated() => Awaitable<void>认证失败时调用
onAccessDenied() => Awaitable<void>403 时调用
showInfoMessage(msg) => void信息消息处理器
showWarningMessage(msg) => void警告消息处理器
showErrorMessage(msg) => void错误消息处理器

HTTP 方法

// GET
const result = await http.get<UserInfo>("/user/info", { params: { id: 1 } });

// POST
const result = await http.post<CreateResult>("/user/create", { data: payload });

// PUT
const result = await http.put<void>("/user/update", { data: payload });

// DELETE
const result = await http.delete<void>("/user/delete", { params: { id: 1 } });

// 上传文件
const result = await http.upload<UploadResult>("/file/upload", {
data: formData,
onProgress: event => console.log(event.loaded / event.total)
});

// 下载文件
await http.download("/file/export", {
params: { id: 1 },
filename: "export.xlsx"
});

ApiResult<T>

所有 HTTP 方法均返回 ApiResult<T>

interface ApiResult<T> {
readonly code: number;
readonly message: string;
readonly data: T;
}

跳过认证

对于不需要携带 Authorization 头的请求:

import { skipAuthenticationHeader, skipAuthenticationValue } from "@vef-framework-react/core";

await http.post("/auth/login", {
data: credentials,
headers: {
[skipAuthenticationHeader]: skipAuthenticationValue
}
});

isBusinessError

import { isBusinessError } from "@vef-framework-react/core";

try {
await http.post("/user/create", { data: payload });
} catch (error) {
if (isBusinessError(error)) {
// 可访问 error.code、error.message
}
}

ApiClient

ApiClientHttpClientQueryClient 组合为一个对象。在大多数项目中,通过 starter.createApiClient() 创建一次后在整个应用中共享。

createApiClient(core 层)

核心层工厂函数。在应用代码中,优先使用 starter.createApiClient(),它在此基础上增加了 Token 存储、消息反馈和未认证处理。

import { createApiClient } from "@vef-framework-react/core";

const apiClient = createApiClient({
http: {
baseUrl: "/api",
okCode: 0
},
query: {
staleTime: 5_000,
gcTime: 300_000
}
});

ApiClientOptions

选项类型说明
httpHttpClientOptions传递给 HttpClient 的选项
queryQueryClientOptions传递给 QueryClient 的选项

createQueryFn

创建带有自动 abort signal 注入的类型化查询函数。

export const findUserPage = apiClient.createQueryFn(
"find_user_page",
http => async (params, pageParam, meta) => {
const result = await http.post("/user/page", { data: params });
return result.data;
}
);

返回的函数带有 .key 属性,用于 queryKey 数组:

useQuery({
queryKey: [findUserPage.key, searchParams],
queryFn: findUserPage
});

createMutationFn

创建类型化的变更函数。

export const createUser = apiClient.createMutationFn(
"create_user",
http => params => http.post("/user/create", { data: params })
);

fetchQueryprefetchQuery

在 React 组件外部进行命令式数据获取:

const userInfo = await apiClient.fetchQuery({
queryKey: [getUserInfo.key, { id: 1 }],
queryFn: getUserInfo
});

await apiClient.prefetchQuery({
queryKey: [getUserInfo.key, { id: 1 }],
queryFn: getUserInfo
});

executeMutation

在 React 组件外部进行命令式变更:

await apiClient.executeMutation({
mutationFn: login,
params: { username, password }
});

QueryKey<TParams>

框架中使用的类型化查询键格式:

type QueryKey<TParams = never> = readonly [Key, ...If<IsNever<TParams>, [], [TParams]>];

MutationFunction<TData, TParams>

扩展了 TanStack 变更函数,增加了 .key 属性,用于 useHasMutating 匹配。

QueryFunction<TData, TParams, TPageParam>

扩展了 TanStack 查询函数,增加了 .key 属性,用于 useHasFetching 匹配。