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
(選填)
一個覆寫 window 上預設值的 fetch 函式。在需要利用 isomorphic-fetch
或 cross-fetch
的 SSR 環境中可能很有用。
timeout
(選填)
以毫秒為單位的數字,表示請求在逾時之前可以花費的最長時間。
isJsonContentType
(選填)
接收 Headers
物件並決定 FetchArgs
參數的 body
欄位是否應透過 JSON.stringify()
轉換成字串的回呼函式。
預設實作會檢查 content-type
標頭,並會比對 "application/json"
和 "application/vnd.api+json"
等值。
jsonContentType
(選填)
在自動設定沒有明確 content-type
標頭的可轉換為 json 的主體的請求的 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
使用對應的擷取回應方法來讀取主體。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
會 reject
任何狀態碼不是 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,
}),
}),
}),
})