☁️
Angularでネットワークエラーハンドリングしてみた
概要
業務でinterceptorを使う機会があってAngularの公式ドキュメントを眺めていたら、気になる部分を見つけました。
要約するとこんなことが書いてあります。
- HttpErrorResponse はstatus === 0だとネットワークエラーを表すよ
- ネットワークエラーなど一時的なエラーへの対処としてretry()オペレータが使えるよ
なるほど。不勉強でした。
ということで、せっかくなのでinterceptorを使って全httpレスポンスにネットワークエラーハンドリングを入れてみました。
interceptorとは
HttpClientを用いて行われる全てのHTTPリクエスト/レスポンスに対して、ミドルウェアのような役割を果たす。
レスポンスのエラーハンドリングであればCustomErrorHandlerを使ってもできそうですが、よりHttpClientの時のみハンドリングすれば良いのでinterceptorを使います。
実際に書いてみた
interceptorには関数ベースかDIベースかがありますが、今回はDIベースで書くことにしました。
@Injectable()
export class NetworkErrorInterceptor implements HttpInterceptor {
private readonly OFF_LINE_MESSAGE = [
'ネットワークに接続できません。',
'接続を確認の上、ページを再読み込みしてください。',
].join('\n');
intercept(
request: HttpRequest<unknown>,
next: HttpHandler,
): Observable<HttpEvent<unknown>> {
return next.handle(request).pipe(
// そもそもネットワークに繋がっていない場合
catchError((error: HttpErrorResponse) => {
if (!navigator.onLine) {
alert(this.OFF_LINE_MESSAGE);
return EMPTY;
}
return throwError(() => error);
}),
// ネットワークエラーの場合、1.5秒置いて2回まで再リクエストする
retry({
count: 2,
delay: (error: HttpErrorResponse) => {
if (error.status === 0) return of(error).pipe(delay(1500));
return throwError(() => error);
},
}),
// retryしてもなおネットワークエラーの場合はalertで知らせる
catchError((error: HttpErrorResponse) => {
if (error.status === 0) {
alert('ネットワークエラーが発生しました。');
this.log(error);
}
return throwError(() => error);
}),
);
}
}
retry()オペレータはcountに最大試行回数。delayには再試行の条件と遅延させる秒数を書きます。今回は使ってないですが、resetOnSuccess
というオプションをtrueにすると再試行で成功したらcountをリセットしてくれます。
RetryConfig
これで下記に対応できました。
- そもそもネットに繋がっていなければalertで知らせる
- ネットワークエラーであれば、1.5秒おいて2回まで再リクエストする
- 再retryしてもネットワークエラーであれば、alertで知らせる
- ネットワークエラーでなければthrowErrorでエラーをストリームに流す
以上、めでたしめでたし。
もっといい方法あればコメントで教えてもらえると嬉しいです!
Discussion