はじめに
AxiosのInterceptorを使った実装を読み解く機会がありましたが、interceptorの役割理解が浅く、思った以上に苦戦しました。
この記事では、
Axiosは何をしていて、その中でInterceptorは何をしているのか
を整理します。
Axiosは何をしているのか
Axiosとは、HTTP通信をJavaScriptから扱いやすくするためのHTTPクライアントです。
たとえば、Axiosを使ってユーザー一覧を取得するとします。
const response = await api.get('/users')
この一行を大まかな流れにすると、次のようになります。
api.get('/users')
↓
リクエストに必要な設定を組み立てる
↓
HTTPリクエスト
↓
サーバー
↓
HTTPレスポンス
↓
レスポンスを組み立てる
↓
呼び出し元
Interceptorを理解するうえで特に重要なのが、リクエスト側のconfigとレスポンス側のresponseです。
リクエストに使われるconfig
Axiosでは、リクエストに関する設定をconfigとして扱います。
たとえば、次のようにAxiosのインスタンスを作成したとします。
const api = axios.create({
baseURL: 'https://example.com/api',
timeout: 5000
})
api.get('/users')
このとき、Axiosは、
- Axiosが持っているデフォルト設定
-
axios.create()で指定した設定 -
api.get()など個別のリクエストで指定した設定
などを組み合わせ、リクエストに使用する設定を決めます。
イメージとしては、次のような情報です。
{
baseURL: 'https://example.com/api',
url: '/users',
method: 'get',
timeout: 5000,
// ...
}
configは、ざっくり言うと
「これからどんなリクエストをするのか」を表す設定情報
です。
レスポンスはresponseとして扱われる
リクエストを送信し、サーバーからHTTPレスポンスが返ってくると、Axiosはその結果をresponseとして扱います。
const response = await api.get('/users')
responseには、APIから返されたデータだけではなく、
response.data
response.status
response.headers
response.config
などの情報が含まれています。
たとえばAPIから返されたユーザー一覧を取得する場合は、
const response = await api.get('/users')
console.log(response.data)
のようにdataを参照します。
Interceptorは何をしているのか
Interceptorとは、
Axiosがリクエストやレスポンスを扱う途中に入り、その内容に対して処理を行える仕組みです。
Interceptorには、Request InterceptorとResponse Interceptorがあります。
先ほどの流れにInterceptorを加えてみます。
api.get('/users')
↓
config
↓
┌─────────────────────┐
│ Request Interceptor │
└─────────────────────┘
↓
HTTPリクエスト
↓
サーバー
↓
HTTPレスポンス
↓
response
↓
┌──────────────────────┐
│ Response Interceptor │
└──────────────────────┘
↓
呼び出し元
Request Interceptor
Request Interceptorは、HTTPリクエストを送信する前に処理を挟めます。
api.interceptors.request.use(
(config) => {
// リクエスト送信前の処理
return config
},
(error) => {
// リクエストエラー時の処理
return Promise.reject(error)
}
)
ここで渡されるconfigは、先ほど説明したAxiosのリクエスト設定です。
たとえば、リクエストにAuthorization Headerを追加したい場合は、
api.interceptors.request.use((config) => {
config.headers.set('Authorization', `Bearer ${token}`)
return config
})
のように処理できます。
Request Interceptorがconfigを作っているわけではありません。
Axiosがリクエストに使うconfigを途中で受け取り、リクエストが送信される前に確認したり変更したりできるということです。
そして、処理したconfigをreturnすることで後続の処理へ渡します。
逆に言うと、return config を書き忘れると後続に undefined が渡って壊れることになります。
Response Interceptor
Response Interceptorは、HTTPレスポンスを受け取った後に処理を挟めます。
api.interceptors.response.use(
(response) => {
// 成功時(デフォルトでは2xx)
return response
},
(error) => {
// 失敗時
return Promise.reject(error)
}
)
Response Interceptorも同じで、responseを作っているわけではありません。
Axiosが受け取ったresponseを、呼び出し元へ渡る前に確認したり変更したりしています。
たとえば、
api.interceptors.response.use((response) => {
return response.data
})
とすると、後続にはresponse全体ではなくresponse.dataが渡ります。
Interceptorでは、単に途中で何かを実行するだけではなく、後続へ渡す値を加工することもできます。
AxiosとInterceptorの関係を整理する
ここまでの処理を改めて一本につなげてみます。
api.get('/users')
↓
Axiosがリクエストのconfigを組み立てる
↓
┌─────────────────────┐
│ Request Interceptor │
│ │
│ configを受け取る │
│ ↓ │
│ 必要な処理を行う │
│ ↓ │
│ return config │
└─────────────────────┘
↓
HTTPリクエスト
↓
サーバー
↓
HTTPレスポンス
↓
Axiosがresponseを組み立てる
↓
┌──────────────────────┐
│ Response Interceptor │
│ │
│ responseを受け取る │
│ ↓ │
│ 必要な処理を行う │
│ ↓ │
│ return response │
└──────────────────────┘
↓
呼び出し元
最初は、
api.interceptors.request.use((config) => {
return config
})
を見ても、configがどこから来て、どこへ返っていくのかがよく分かっていませんでした。
しかし、Interceptor単体ではなくAxiosの処理から考えると、
config
↓
Request Interceptor
↓
HTTP通信
↓
response
↓
Response Interceptor
という一連の流れの中にInterceptorがいることが分かります。
まとめ
今回、Interceptorの実装を読み解くために、まずAxiosが何をしているのかから整理しました。
Axiosは、リクエストに使うconfigを組み立て、HTTP通信を行った結果をresponseとして扱います。
そしてInterceptorはAxiosの処理の途中に入り、
- Request Interceptorでは、リクエスト前の
configに対して処理を行う - Response Interceptorでは、レスポンス後の
responseに対して処理を行う
という役割を持っています。
「Interceptorはリクエストやレスポンスに共通処理を挟むもの」とだけ覚えていたときよりも、
「Axiosが扱っている値を、処理の途中で受け取って、必要な処理をして次へ渡すもの」
と捉えることで、実装の流れを追いやすくなりました。