この記事について
Next.jsで、ログイン必須のページにアクセスされたとき、ログインしていなければ強制的にログインページにリダイレクトする仕組みを紹介する。
準備
- Nextプロジェクトのルートディレクトリに
plugin
ファルダを作成し、その中にaxios.ts
ファイルを作成する -
npm install axios
またはyarn add axios
等でaxiosをインストールする
実装
import axios from "axios";
const axios_instance = axios.create({
headers:{
"Content-Type": "application/json",
},
});
axios_instance.interceptors.request.use(
function(config){
return config;
},
function (error){
return Promise.reject(error);
}
);
axios_instance.interceptors.response.use(
function (response){
return response;
},
function (error){
const originalConfig = error.config;
if(
error.response &&
error.response.status === 401 &&
!originalConfig.retry
){
//認証エラーならリトライ
originalConfig.retry = true;
//以下の場合はリトライしない
if(originalConfig.url === "ログインAPIへのパス"){
return Promise.reject(error);
}
axios_instance
.post("リトライAPIへのパス", {refresh:""})
.then((response) => {
return axios_instance(originalConfig);
})
.catch(function(error){
return Promise.reject(error);
});
}
else if(error.response && error.response.status !== 422){
window.location.href = "ログインAPIへのパス";
}
else{
return Promise.reject(error);
}
}
);
export default axios_instance;
解説
axiosインスタンスの作成
const axios_instance = axios.create({
headers:{
"Content-Type": "application/json",
},
});
Content-Type が "application/json" に設定された axios インスタンスを作成している。
これにより、送信されるリクエストはすべて JSON 形式でデータを扱うことが前提となる。
インターセプタ
axios_instance.interceptors.request.use(
function(config){
return config;
},
function (error){
return Promise.reject(error);
}
);
リクエストが送信される前にインターセプターが実行され、渡されたconfig
(リクエストの設定情報)をそのまま返す。また、エラーが発生した場合は、単にPromise.reject(error)
でエラーを返す。
axios_instance.interceptors.response.use(
function (response){
return response;
},
function (error){
const originalConfig = error.config;
if(
error.response &&
error.response.status === 401 &&
!originalConfig.retry
){
//認証エラーならリトライ
originalConfig.retry = true;
//以下の場合はリトライしない
if(originalConfig.url === "ログインAPIへのパス"){
return Promise.reject(error);
}
axios_instance
.post("リトライAPIへのパス", {refresh:""})
.then((response) => {
return axios_instance(originalConfig);
})
.catch(function(error){
return Promise.reject(error);
});
}
else if(error.response && error.response.status !== 422){
window.location.href = "ログインAPIへのパス";
}
else{
return Promise.reject(error);
}
}
);
レスポンスが正常に返ってきた場合は、レスポンスそのものをそのまま返すが、エラー時には以下のように処理される
-
認証エラー(401)の場合:
エラーが401(Unauthorized)で、かつリトライがまだ行われていない場合(originalConfig.retry が false の場合)に処理が進む。
ログインAPIのエンドポイントに対してPOSTリクエストを送信して認証情報のリフレッシュ、成功した場合は元のリクエスト(originalConfig)を再実行する。
ただし、リクエスト先がログインAPIへのパス
の場合は、ログイン自体のエラーと判断しリトライは行わない。
originalConfig.retry を true に設定し、同じリクエストの二重リトライを防止。
-
422エラーの場合:
422エラーは特定のバリデーションエラーや処理上のエラーと判断され、そのままエラーを返す。
-
その他のエラー(422以外)の場合:
エラーステータスが422以外であれば、ユーザーをログインページにリダイレクトする。これは、認証が失敗している場合など、再ログインが必要と判断された場合の処理。
使い方
axiosをインポートする際に、import axios from "@/plugin/axios";
としてインポートする。
以降は普通のaxiosと同じようにリクエストを扱える
この場合、バックエンドへのリクエストはaxios
で行い、Next.js内への通信はfetch
で行うなどの戦略が考えられる