web-dev-qa-db-ja.com

のキャッチエラー Angular HttpClient

私はこのように見えるデータサービスを持っています:

@Injectable()
export class DataService {
    baseUrl = 'http://localhost'
        constructor(
        private httpClient: HttpClient) {
    }
    get(url, params): Promise<Object> {

        return this.sendRequest(this.baseUrl + url, 'get', null, params)
            .map((res) => {
                return res as Object
            })
            .toPromise();
    }
    post(url, body): Promise<Object> {
        return this.sendRequest(this.baseUrl + url, 'post', body)
            .map((res) => {
                return res as Object
            })
            .toPromise();
    }
    patch(url, body): Promise<Object> {
        return this.sendRequest(this.baseUrl + url, 'patch', body)
            .map((res) => {
                return res as Object
            })
            .toPromise();
    }
    sendRequest(url, type, body, params = null): Observable<any> {
        return this.httpClient[type](url, { params: params }, body)
    }
}

HTTPエラー(つまり404)が発生した場合、厄介なコンソールメッセージが表示されます。ERRORエラー:(見込みではありません):[object Object] from core.es5.jsどのように処理しますか。私の場合?

61
LastTribunal

ニーズに応じて、いくつかのオプションがあります。リクエストごとにエラーを処理する場合は、catchをリクエストに追加します。グローバルソリューションを追加する場合は、HttpInterceptorを使用します。

こちらの作業デモ用プランカー を以下のソリューションで開きます。

tl; dr

最も単純なケースでは、次のように.catch()または.subscribe()を追加するだけです。

import 'rxjs/add/operator/catch'; // don't forget this, or you'll get a runtime error
this.httpClient
      .get("data-url")
      .catch((err: HttpErrorResponse) => {
        // simple logging, but you can do a lot more, see below
        console.error('An error occurred:', err.error);
      });

// or
this.httpClient
      .get("data-url")
      .subscribe(
        data => console.log('success', data),
        error => console.log('oops', error)
      );

しかし、これにはさらに詳細があります。以下を参照してください。


メソッド(ローカル)ソリューション:エラーを記録してフォールバック応答を返す

1か所でのみエラーを処理する必要がある場合は、catchを使用して、完全に失敗する代わりにデフォルト値(または空の応答)を返すことができます。また、キャストするためだけに.mapは必要ありません。汎用関数を使用できます。ソース: Angular.io-エラー詳細の取得

したがって、一般的な.get()メソッドは次のようになります。

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty';
import 'rxjs/add/operator/retry'; // don't forget the imports

@Injectable()
export class DataService {
    baseUrl = 'http://localhost';
    constructor(private httpClient: HttpClient) { }

    // notice the <T>, making the method generic
    get<T>(url, params): Observable<T> {
      return this.httpClient
          .get<T>(this.baseUrl + url, {params})
          .retry(3) // optionally add the retry
          .catch((err: HttpErrorResponse) => {

            if (err.error instanceof Error) {
              // A client-side or network error occurred. Handle it accordingly.
              console.error('An error occurred:', err.error.message);
            } else {
              // The backend returned an unsuccessful response code.
              // The response body may contain clues as to what went wrong,
              console.error(`Backend returned code ${err.status}, body was: ${err.error}`);
            }

            // ...optionally return a default fallback value so app can continue (pick one)
            // which could be a default value
            // return Observable.of<any>({my: "default value..."});
            // or simply an empty observable
            return Observable.empty<T>();
          });
     }
}

エラーを処理すると、URLのサービスの状態が悪い場合でもアプリを続行できます。

このリクエストごとのソリューションは、主に各メソッドに特定のデフォルト応答を返したい場合に適しています。ただし、エラーの表示のみに関心がある場合(またはグローバルなデフォルトの応答がある場合)、以下で説明するようにインターセプターを使用することをお勧めします。

working demo plunker here を実行します。


高度な使用法:すべての要求または応答をインターセプトする

繰り返しますが、 Angular.ioガイド は以下を示します。

@angular/common/httpの主要な機能はインターセプトです。これは、アプリケーションとバックエンドの間にあるインターセプターを宣言する機能です。アプリケーションが要求を行うと、インターセプターはサーバーに送信する前にそれを変換し、インターセプターはアプリケーションがそれを見る前にその応答を変換できます。これは、認証からログ記録まですべてに役立ちます。

もちろん、これを使用して非常に簡単な方法でエラーを処理できます( demo plunker here ):

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse,
         HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty';
import 'rxjs/add/operator/retry'; // don't forget the imports

@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .catch((err: HttpErrorResponse) => {

        if (err.error instanceof Error) {
          // A client-side or network error occurred. Handle it accordingly.
          console.error('An error occurred:', err.error.message);
        } else {
          // The backend returned an unsuccessful response code.
          // The response body may contain clues as to what went wrong,
          console.error(`Backend returned code ${err.status}, body was: ${err.error}`);
        }

        // ...optionally return a default fallback value so app can continue (pick one)
        // which could be a default value (which has to be a HttpResponse here)
        // return Observable.of(new HttpResponse({body: [{name: "Default value..."}]}));
        // or simply an empty observable
        return Observable.empty<HttpEvent<any>>();
      });
  }
}

インターセプターの提供:上記のHttpErrorInterceptorを宣言するだけでは、アプリはそれを使用しません。次のように、インターセプターとして提供することで、 アプリモジュールに接続する する必要があります。

import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { HttpErrorInterceptor } from './path/http-error.interceptor';

@NgModule({
  ...
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: HttpErrorInterceptor,
    multi: true,
  }],
  ...
})
export class AppModule {}

注:both両方のエラーインターセプターといくつかのローカルエラー処理がある場合、当然のことながら、エラーは常にインターセプターによって処理されるため、ローカルエラー処理に到達する前にローカルエラー処理がトリガーされることはありません。

working demo plunker here を実行します。

141
acdcjunior

HTTPClient APIの登場により、Http APIが置き換えられただけでなく、新しいものが追加されました、HttpInterceptor API。

その目標の1つは、すべてのHTTP発信要求と着信応答にデフォルトの動作を追加することです。

そのため、 デフォルトのエラー処理動作 を追加したいと仮定すると、すべての可能なhttp.get/post/etcメソッドに.catch()を追加することは、非常に保守が困難です。

これはHttpInterceptorを使った例として次のようにすることができます。

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { _throw } from 'rxjs/observable/throw';
import 'rxjs/add/operator/catch';

/**
 * Intercepts the HTTP responses, and in case that an error/exception is thrown, handles it
 * and extract the relevant information of it.
 */
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
    /**
     * Intercepts an outgoing HTTP request, executes it and handles any error that could be triggered in execution.
     * @see HttpInterceptor
     * @param req the outgoing HTTP request
     * @param next a HTTP request handler
     */
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(req)
            .catch(errorResponse => {
                let errMsg: string;
                if (errorResponse instanceof HttpErrorResponse) {
                    const err = errorResponse.message || JSON.stringify(errorResponse.error);
                    errMsg = `${errorResponse.status} - ${errorResponse.statusText || ''} Details: ${err}`;
                } else {
                    errMsg = errorResponse.message ? errorResponse.message : errorResponse.toString();
                }
                return _throw(errMsg);
            });
    }
}

/**
 * Provider POJO for the interceptor
 */
export const ErrorInterceptorProvider = {
    provide: HTTP_INTERCEPTORS,
    useClass: ErrorInterceptor,
    multi: true,
};

// app.module.ts

import { ErrorInterceptorProvider } from 'somewhere/in/your/src/folder';

@NgModule({
   ...
   providers: [
    ...
    ErrorInterceptorProvider,
    ....
   ],
   ...
})
export class AppModule {}

OPのためのいくつかの追加情報:強い型なしでhttp.get/post/etcを呼び出すことはAPIの最適な使い方ではありません。あなたのサービスは次のようになります。

// These interfaces could be somewhere else in your src folder, not necessarily in your service file
export interface FooPost {
 // Define the form of the object in JSON format that your 
 // expect from the backend on post
}

export interface FooPatch {
 // Define the form of the object in JSON format that your 
 // expect from the backend on patch
}

export interface FooGet {
 // Define the form of the object in JSON format that your 
 // expect from the backend on get
}

@Injectable()
export class DataService {
    baseUrl = 'http://localhost'
    constructor(
        private http: HttpClient) {
    }

    get(url, params): Observable<FooGet> {

        return this.http.get<FooGet>(this.baseUrl + url, params);
    }

    post(url, body): Observable<FooPost> {
        return this.http.post<FooPost>(this.baseUrl + url, body);
    }

    patch(url, body): Observable<FooPatch> {
        return this.http.patch<FooPatch>(this.baseUrl + url, body);
    }
}

Promisesの代わりにあなたのサービスメソッドからObservablesを返すことは別の悪い決断です。

そして追加のアドバイス: _ type _ scriptを使っているなら、typeの部分を使い始めてください。あなたは言語の最大の利点の1つを失います:あなたが扱っている価値のタイプを知ること。

私の考えでは、角度付きサービスの良い例が欲しいなら、 以下の要点 を見てください。

41
Jota.Toledo

HttpInterceptor を使うことに関する acdcjunior の答えを最新のRxJs機能(v.6)で更新してください。

import { Injectable } from '@angular/core';
import {
  HttpInterceptor,
  HttpRequest,
  HttpErrorResponse,
  HttpHandler,
  HttpEvent,
  HttpResponse
} from '@angular/common/http';

import { Observable, EMPTY, throwError, of } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    return next.handle(request).pipe(
      catchError((error: HttpErrorResponse) => {
        if (error.error instanceof Error) {
          // A client-side or network error occurred. Handle it accordingly.
          console.error('An error occurred:', error.error.message);
        } else {
          // The backend returned an unsuccessful response code.
          // The response body may contain clues as to what went wrong,
          console.error(`Backend returned code ${error.status}, body was: ${error.error}`);
        }

        // If you want to return a new response:
        //return of(new HttpResponse({body: [{name: "Default value..."}]}));

        // If you want to return the error on the upper level:
        //return throwError(error);

        // or just return nothing:
        return EMPTY;
      })
    );
  }
}
20
MegaCasper

あなたはおそらくこのようなものが欲しいのです。

this.sendRequest(...)
.map(...)
.catch((err) => {
//handle your error here
})

それはあなたがあなたのサービスをどのように利用するかにも大きく依存しますが、これは基本的なケースです。

2
dee zg

(以前のAPIで行われた方法と比較して)かなり簡単です。

(コピーして貼り付けた) Angular公式ガイドからのソース

 http
  .get<ItemsResponse>('/api/items')
  .subscribe(
    // Successful responses call the first callback.
    data => {...},
    // Errors will call this callback instead:
    err => {
      console.log('Something went wrong!');
    }
  );
2
Tomer

@acdcjuniorの回答に従って、これを実装します。

サービス:

  get(url, params): Promise<Object> {

            return this.sendRequest(this.baseUrl + url, 'get', null, params)
                .map((res) => {
                    return res as Object
                }).catch((e) => {
                    return Observable.of(e);
                })
                .toPromise();
        }

呼び出し元:

this.dataService.get(baseUrl, params)
            .then((object) => {
                if(object['name'] === 'HttpErrorResponse') {
                            this.error = true;
                           //or any handle
                } else {
                    this.myObj = object as MyClass 
                }
           });
1
LastTribunal

Angular 6+の場合、.catchはObservableでは直接機能しません。 .pipe(catchError(this.errorHandler)) を使う必要があります

以下のコード:

import { IEmployee } from './interfaces/employee';
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class EmployeeService {

  private url = '/assets/data/employee.json';

  constructor(private http: HttpClient) { }

  getEmployees(): Observable<IEmployee[]> {
    return this.http.get<IEmployee[]>(this.url)
                    .pipe(catchError(this.errorHandler));  // catch error
  }



  /** Error Handling method */

  errorHandler(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error('An error occurred:', error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(
        `Backend returned code ${error.status}, ` +
        `body was: ${error.error}`);
    }
    // return an observable with a user-facing error message
    return throwError(
      'Something bad happened; please try again later.');

  }

}


詳細は、 Angular Guide for Http を参照してください。

1

ここで提供されている解決策のいずれかでエラーを見つけることができないと思う場合は、サーバーがCORS要求を処理していない可能性があります。

その場合、Javascript(はるかに少ないAngular)がエラー情報にアクセスできます。

コンソールで、CORBまたはCross-Origin Read Blockingを含む警告を探します。

また、エラーを処理するための構文も変更されています(他のすべての回答で説明されているとおり)。これで、パイプ演算子を使うことができます。

this.service.requestsMyInfo(payload).pipe(
    catcheError(err => {
        // handle the error here.
    })
);
0
Kevin Beal
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

const PASSENGER_API = 'api/passengers';

getPassengers(): Observable<Passenger[]> {
  return this.http
    .get<Passenger[]>(PASSENGER_API)
    .pipe(catchError((error: HttpErrorResponse) => throwError(error)));
}
0
sun sreng

インターセプターを使用することで、エラーを見つけることができます。以下はコードです:

@Injectable()
export class ResponseInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    //Get Auth Token from Service which we want to pass thr service call
    const authToken: any = `Bearer ${sessionStorage.getItem('jwtToken')}`
    // Clone the service request and alter original headers with auth token.
    const authReq = req.clone({
      headers: req.headers.set('Content-Type', 'application/json').set('Authorization', authToken)
    });

    const authReq = req.clone({ setHeaders: { 'Authorization': authToken, 'Content-Type': 'application/json'} });

    // Send cloned request with header to the next handler.
    return next.handle(authReq).do((event: HttpEvent<any>) => {
      if (event instanceof HttpResponse) {
        console.log("Service Response thr Interceptor");
      }
    }, (err: any) => {
      if (err instanceof HttpErrorResponse) {
        console.log("err.status", err);
        if (err.status === 401 || err.status === 403) {
          location.href = '/login';
          console.log("Unauthorized Request - In case of Auth Token Expired");
        }
      }
    });
  }
}

あなたは このブログ ..それを好むことができます。

0