☁️

Angularでネットワークエラーハンドリングしてみた

2024/09/16に公開

概要

業務でinterceptorを使う機会があってAngularの公式ドキュメントを眺めていたら、気になる部分を見つけました。
https://angular.dev/guide/http/making-requests#handling-request-failure

要約するとこんなことが書いてあります。

  • HttpErrorResponse はstatus === 0だとネットワークエラーを表すよ
  • ネットワークエラーなど一時的なエラーへの対処としてretry()オペレータが使えるよ

なるほど。不勉強でした。
ということで、せっかくなのでinterceptorを使って全httpレスポンスにネットワークエラーハンドリングを入れてみました。

interceptorとは

HttpClientを用いて行われる全てのHTTPリクエスト/レスポンスに対して、ミドルウェアのような役割を果たす。
https://angular.dev/guide/http/interceptors

レスポンスのエラーハンドリングであれば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