fetchBaseQuery
这是一个围绕 fetch
的非常小的包装器,旨在简化 HTTP 请求。它不是 axios
、superagent
或任何其他更重量级库的完整替代品,但它将涵盖您绝大多数 HTTP 请求需求。
fetchBaseQuery
是一个工厂函数,它生成一个与 RTK Query 的 baseQuery
配置选项兼容的数据获取方法。它接受来自 fetch 的 RequestInit
接口的所有标准选项,以及 baseUrl
、prepareHeaders
函数、可选的 fetch
函数、paramsSerializer
函数和 timeout
。
基本用法
要使用它,在您 创建 API 服务定义 时导入它,将其作为 fetchBaseQuery(options)
调用,并将结果作为 createApi
中的 baseQuery
字段传递。
- TypeScript
- JavaScript
// Or from '@reduxjs/toolkit/query/react'
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const pokemonApi = createApi({
// Set the baseUrl for every endpoint below
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query({
// Will make a request like https://pokeapi.co/api/v2/pokemon/bulbasaur
query: (name: string) => `pokemon/${name}`,
}),
updatePokemon: builder.mutation({
query: ({ name, patch }) => ({
url: `pokemon/${name}`,
// When performing a mutation, you typically use a method of
// PATCH/PUT/POST/DELETE for REST endpoints
method: 'PATCH',
// fetchBaseQuery automatically adds `content-type: application/json` to
// the Headers and calls `JSON.stringify(patch)`
body: patch,
}),
}),
}),
})
// Or from '@reduxjs/toolkit/query/react'
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const pokemonApi = createApi({
// Set the baseUrl for every endpoint below
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query({
// Will make a request like https://pokeapi.co/api/v2/pokemon/bulbasaur
query: (name) => `pokemon/${name}`,
}),
updatePokemon: builder.mutation({
query: ({ name, patch }) => ({
url: `pokemon/${name}`,
// When performing a mutation, you typically use a method of
// PATCH/PUT/POST/DELETE for REST endpoints
method: 'PATCH',
// fetchBaseQuery automatically adds `content-type: application/json` to
// the Headers and calls `JSON.stringify(patch)`
body: patch,
}),
}),
}),
})
签名
type FetchBaseQuery = (
args: FetchBaseQueryArgs,
) => (
args: string | FetchArgs,
api: BaseQueryApi,
extraOptions: ExtraOptions,
) => FetchBaseQueryResult
type FetchBaseQueryArgs = {
baseUrl?: string
prepareHeaders?: (
headers: Headers,
api: Pick<
BaseQueryApi,
'getState' | 'extra' | 'endpoint' | 'type' | 'forced'
>,
) => MaybePromise<Headers | void>
fetchFn?: (
input: RequestInfo,
init?: RequestInit | undefined,
) => Promise<Response>
paramsSerializer?: (params: Record<string, any>) => string
isJsonContentType?: (headers: Headers) => boolean
jsonContentType?: string
timeout?: number
} & RequestInit
type FetchBaseQueryResult = Promise<
| {
data: any
error?: undefined
meta?: { request: Request; response: Response }
}
| {
error: {
status: number
data: any
}
data?: undefined
meta?: { request: Request; response: Response }
}
>
参数
baseUrl
(必需)
通常是一个字符串,例如 https://api.your-really-great-app.com/v1/
。如果您不提供 baseUrl
,它将默认为请求发出位置的相对路径。您应该始终指定它。
prepareHeaders
(可选)
允许您在每个请求上注入标头。您可以在端点级别指定标头,但通常您需要在此处设置通用标头,例如 authorization
。作为一种便捷机制,第二个参数允许您使用 getState
访问您的 redux 存储,以防您存储需要的信息(例如身份验证令牌)。此外,它还提供对 extra
、endpoint
、type
和 forced
的访问,以解锁更细粒度的条件行为。
您可以直接修改 headers
参数,并且返回它也是可选的。
type prepareHeaders = (
headers: Headers,
api: {
getState: () => unknown
extra: unknown
endpoint: string
type: 'query' | 'mutation'
forced: boolean | undefined
},
) => Headers | void
paramsSerializer
(可选)
一个函数,可用于对传递到 params
的数据应用自定义转换。如果您不提供此函数,params
将直接传递给 new URLSearchParams()
。对于某些 API 集成,您可能需要利用它来使用类似于 query-string
库的东西来支持不同的数组类型。
fetchFn
(可选)
一个覆盖窗口上默认 fetch 函数的 fetch 函数。在 SSR 环境中可能很有用,您可能需要利用 isomorphic-fetch
或 cross-fetch
。
timeout
(可选)
一个以毫秒为单位的数字,表示请求在超时之前可以花费的最长时间。
isJsonContentType
(可选)
一个回调函数,它接收一个 Headers
对象,并确定 FetchArgs
参数的 body
字段是否应该通过 JSON.stringify()
进行字符串化。
默认实现检查 content-type
标头,并将匹配 "application/json"
和 "application/vnd.api+json"
之类的值。
jsonContentType
(可选)
当为具有可 JSON 化主体但没有显式 content-type
标头的请求自动设置 content-type
标头时使用。默认值为 "application/json"
。
常见用法模式
在请求上设置默认标头
prepareHeaders
最常见的用例是自动为您的 API 请求包含 authorization
标头。
- TypeScript
- JavaScript
import { fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { RootState } from './store'
const baseQuery = fetchBaseQuery({
baseUrl: '/',
prepareHeaders: (headers, { getState }) => {
const token = (getState() as RootState).auth.token
// If we have a token set in state, let's assume that we should be passing it.
if (token) {
headers.set('authorization', `Bearer ${token}`)
}
return headers
},
})
import { fetchBaseQuery } from '@reduxjs/toolkit/query'
const baseQuery = fetchBaseQuery({
baseUrl: '/',
prepareHeaders: (headers, { getState }) => {
const token = getState().auth.token
// If we have a token set in state, let's assume that we should be passing it.
if (token) {
headers.set('authorization', `Bearer ${token}`)
}
return headers
},
})
单个查询选项
您可以在每个请求的基础上定义更多行为。query
字段可以返回一个包含 RequestInit
接口中可用的任何默认 fetch
选项以及以下附加选项的对象
- TypeScript
- JavaScript
interface FetchArgs extends RequestInit {
url: string
params?: Record<string, any>
body?: any
responseHandler?:
| 'json'
| 'text'
| `content-type`
| ((response: Response) => Promise<any>)
validateStatus?: (response: Response, body: any) => boolean
timeout?: number
}
const defaultValidateStatus = (response: Response) =>
response.status >= 200 && response.status <= 299
const defaultValidateStatus = (response) =>
response.status >= 200 && response.status <= 299
设置主体
默认情况下,fetchBaseQuery
假设您发出的每个请求都将是 json
,因此在这些情况下,您只需设置 url
并在适当的时候传递 body
对象即可。对于其他实现,您可以手动设置 Headers
来指定内容类型。
json
// omitted
endpoints: (builder) => ({
updateUser: builder.query({
query: (user: Record<string, string>) => ({
url: `users`,
method: 'PUT',
body: user // Body is automatically converted to json with the correct headers
}),
}),
text
// omitted
endpoints: (builder) => ({
updateUser: builder.query({
query: (user: Record<string, string>) => ({
url: `users`,
method: 'PUT',
headers: {
'content-type': 'text/plain',
},
body: user
}),
}),
设置查询字符串
fetchBaseQuery
提供了一种简单的机制,通过将对象传递给 new URLSearchParms()
来将 object
转换为序列化查询字符串。如果这不能满足您的需求,您有两个选择
- 将
paramsSerializer
选项传递给fetchBaseQuery
以应用自定义转换 - 构建您自己的查询字符串并在
url
中设置它
// omitted
endpoints: (builder) => ({
updateUser: builder.query({
query: (user: Record<string, string>) => ({
url: `users`,
// Assuming no `paramsSerializer` is specified, the user object is automatically converted
// and produces a url like /api/users?first_name=test&last_name=example
params: user
}),
}),
解析响应
默认情况下,fetchBaseQuery
假设您获得的每个 Response
都将被解析为 json
。如果您不希望发生这种情况,您可以通过指定替代响应处理程序(如 text
)来自定义行为,或者完全控制并使用接受原始 Response
对象的自定义函数 - 允许您使用任何 Response
方法。
responseHandler
字段可以是
- TypeScript
- JavaScript
type ResponseHandler =
| 'content-type'
| 'json'
| 'text'
| ((response: Response) => Promise<any>)
"json"
和 "text"
值指示 fetchBaseQuery
使用相应的 fetch 响应方法来读取主体。content-type
将检查标头字段以首先确定这是否看起来是 JSON,然后使用这两个方法之一。回调允许您自己处理主体。
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const customApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => ({
url: `users`,
// This is the same as passing 'text'
responseHandler: (response) => response.text(),
}),
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const customApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => ({
url: `users`,
// This is the same as passing 'text'
responseHandler: (response) => response.text(),
}),
}),
}),
})
如果您向仅返回具有未定义主体的 `200` 的 API 发出 `json` 请求,`fetchBaseQuery` 将将其作为 `undefined` 传递,并且不会尝试将其解析为 `json`。这在某些 API 中很常见,尤其是在 `delete` 请求中。
处理非标准响应状态码
默认情况下,`fetchBaseQuery` 会 `拒绝` 任何状态码不是 `2xx` 的 `Response` 并将其设置为 `error`。这与您在 `axios` 和其他流行库中体验到的行为相同。如果您正在处理非标准 API,可以使用 `validateStatus` 选项自定义此行为。
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const customApi = createApi({
// Set the baseUrl for every endpoint below
baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => ({
url: `users`,
// Example: we have a backend API always returns a 200,
// but sets an `isError` property when there is an error.
validateStatus: (response, result) =>
response.status === 200 && !result.isError,
}),
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const customApi = createApi({
// Set the baseUrl for every endpoint below
baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => ({
url: `users`,
// Example: we have a backend API always returns a 200,
// but sets an `isError` property when there is an error.
validateStatus: (response, result) =>
response.status === 200 && !result.isError,
}),
}),
}),
})
向请求添加自定义超时
默认情况下,`fetchBaseQuery` 没有设置默认超时值,这意味着您的请求将一直处于挂起状态,直到您的 api 解决请求或达到浏览器的默认超时(通常为 5 分钟)。大多数情况下,这不是您想要的。使用 `fetchBaseQuery` 时,您可以设置 `baseQuery` 或单个端点的 `timeout`。当同时指定这两个选项时,端点值将优先。
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const api = createApi({
// Set a default timeout of 10 seconds
baseQuery: fetchBaseQuery({ baseUrl: '/api/', timeout: 10000 }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => ({
url: `users`,
// Example: we know the users endpoint is _really fast_ because it's always cached.
// We can assume if its over > 1000ms, something is wrong and we should abort the request.
timeout: 1000,
}),
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const api = createApi({
// Set a default timeout of 10 seconds
baseQuery: fetchBaseQuery({ baseUrl: '/api/', timeout: 10000 }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => ({
url: `users`,
// Example: we know the users endpoint is _really fast_ because it's always cached.
// We can assume if its over > 1000ms, something is wrong and we should abort the request.
timeout: 1000,
}),
}),
}),
})