React axios の response インターセプター で n回リトライする
React で axios を使用して Http 通信したときに、接続 Timeout が発生した場合は n 回リトライしたい。
axios の response インターセプターでできそうだと思ったのですが、盛大にハマってしまいました😭
やっと対応できたので記事にしておきます。
axiosのバージョン: v0.25.0
axios のインターセプターの基本的な使用方法
axios のインターセプターの基本的な使用方法については、コチラ React axios インターセプターでフックを使えるようにするで記載しましたので、軽く再掲しておきます。
axios 拡張用のファイルを作成します。
ファイル名はなんでもいいですが、axiosClient.ts というファイル名で作成しました。
- すべての HTTP リクエストに共通するデフォルト config を設定します
- すべてのリクエストに割り込み処理を入れるリクエストインターセプターを設定します。
リクエストインターセプターはリクエストヘッダーにアクセストークンを設定したりといった用途に使われます。 - すべてのレスポンスに割り込み処理を入れるレスポンスインターセプターを設定します。
レスポンスインターセプターはログを残したり、エラーを調整したりといった用途に使われます。
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
const BaseUrl = 'https://xxxxx.xxx.com'
/**
* デフォルト config の設定
*/
export const axiosClient = axios.create({
baseURL: BaseUrl,
timeout: 3000,
headers: {
'Content-Type': 'application/json'
}
})
/**
* リクエスト インターセプター
*/
axiosClient.interceptors.request.use((config: AxiosRequestConfig) => {
if (config.headers !== undefined) {
// --ヘッダにアクセストークンを埋める
// const accessToken = getAccessToken()
// if (accessToken) {
// config.headers.Authorization = `Bearer ${accessToken}`
// }
}
return config
})
/**
* レスポンス インターセプター
*/
axiosClient.interceptors.response.use(
(response: AxiosResponse) => {
return response
},
(error: AxiosError) => {
switch (error.response?.status) {
case 401:
// なにかする
break
default:
break
}
return Promise.reject(error)
}
)
使用するときは、axios.create メソッドで作成した axios インスタンスからリクエストを送信します。
+ import { axiosClient } from '@/_lib/axiosConfig'
export type Sample = {
id: number
name: string
}
export function useSampleApi() {
const GetAll = async (): Promise<Sample[]> => {
try {
+ const response = await axiosClient.get(`/api/Sample/GetAll`)
return response.data
} catch (error: unknown) {
throw error
}
}
}
接続 Timeout が発生した場合に 1 回リトライする
まずは response インターセプターを使用して 1 回だけリトライできるように実装します。
接続 Timeout 時は エラーコード ECONNABORTED
が返ります。
またエラーの config にリクエスト情報があるので、その config を利用して再度リクエストを送信するようにします。
axiosClient.interceptors.response.use(
(response: AxiosResponse) => {
return response
},
(error: AxiosError) => {
// リトライ処理
+ if (error.code === 'ECONNABORTED') {
+ return axiosClient.request(error.config)
+ }
return Promise.reject(error)
}
)
接続 Timeout が発生した場合に n 回リトライする
何回リトライするか?現在のレスポンスは何回目のリトライのものか?といった情報を保持するために AxiosRequestConfig を利用します。(コレがわからなかった!!)
▶ AxiosRequestConfig を拡張する
まずは AxiosReuestConfig を拡張して、何回リトライするかを保持する retries
プロパティと、何回目のリトライかを保持するretryCount
プロパティを追加します。
リトライ条件などを追加してもいいかもしれません。
ファイル名は何でもいいのですが、型定義ファイルは慣習的に xxxxx.d.ts とするのが一般的なので axios.d.ts
としました。
import "axios";
declare module "axios" {
export interface AxiosRequestConfig {
retries?: number;
retryCount?: number;
}
}
▶ レスポンスインターセプターの設定
axios.createメソッドの引数に指定する AxiosRequestConfig に、先ほど作成したプロパティretries(リトライ回数)
とretryCount(何回目のリトライか)
に値を設定します。
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
const BaseUrl = 'https://xxxxx.xxx.com'
/**
* デフォルト config の設定
*/
export const axiosClient: AxiosInstance = axios.create({
baseURL: BaseUrl,
timeout: 3000,
headers: {
'Content-Type': 'application/json'
},
+ retries: 3,
+ retryCount: 0,
})
/**
* リクエスト インターセプター
*/
axiosClient.interceptors.request.use((config: AxiosRequestConfig) => {
console.log('axiosClient.interceptors.request.use')
if (config.headers !== undefined) {
// --ヘッダにアクセストークンを埋める
// const accessToken = getAccessToken()
// if (accessToken) {
// config.headers.Authorization = `Bearer ${accessToken}`
// }
}
return config
},
(error: AxiosError) => {
return Promise.reject(error)
})
/**
* レスポンス インターセプター
*/
axiosClient.interceptors.response.use(
(response: AxiosResponse) => {
return response
},
(error: AxiosError) => {
// リトライ処理
+ if (error.code === 'ECONNABORTED') {
+ if ((error.config.retries ?? 0) > (error.config.retryCount ?? 0)) {
+ error.config.retryCount = (error.config.retryCount ?? 0) + 1
+ return axiosClient.request(error.config)
+ }
+ }
return Promise.reject(error)
}
)
以上でリトライができました。
リクエストのUrlを修正すればコチラで動作確認できます。
Discussion