在 Angular HttpClient 中捕获错误 [英] Catching errors in Angular HttpClient

查看:47
本文介绍了在 Angular HttpClient 中捕获错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个如下所示的数据服务:

@Injectable()导出类数据服务{baseUrl = 'http://localhost'构造函数(私人 httpClient: HttpClient) {}get(url, params): Promise{返回 this.sendRequest(this.baseUrl + url, 'get', null, params).map((res) => {将 res 作为对象返回}).承诺();}post(url, body): Promise{返回 this.sendRequest(this.baseUrl + url, 'post', body).map((res) => {将 res 作为对象返回}).承诺();}patch(url, body): Promise{返回 this.sendRequest(this.baseUrl + url, 'patch', body).map((res) => {将 res 作为对象返回}).承诺();}sendRequest(url, type, body, params = null): Observable{返回 this.httpClient[type](url, { params: params }, body)}}

如果我收到 HTTP 错误(即 404),我会收到令人讨厌的控制台消息:错误错误:未捕获(承诺):[object Object] 来自 core.es5.js我该如何处理?

解决方案

您有一些选择,具体取决于您的需要.如果您想在每个请求的基础上处理错误,请将 catch 添加到您的请求中.如果要添加全局解决方案,请使用 HttpInterceptor.

打开这里是解决方案的工作演示plunker下面.

tl;博士

在最简单的情况下,您只需要添加一个 .catch() 或一个 .subscribe(),例如:

import 'rxjs/add/operator/catch';//不要忘记这一点,否则你会得到一个运行时错误this.httpClient.get("数据-url").catch((err: HttpErrorResponse) => {//简单的日志记录,但你可以做更多,见下文console.error('发生错误:', err.error);});//或者this.httpClient.get("数据-url").订阅(数据 =>console.log('成功', 数据),错误 =>控制台日志('哎呀',错误));

但是还有更多详细信息,请参见下文.


方法(本地)解决方案:记录错误并返回回退响应

如果你只需要在一个地方处理错误,你可以使用 catch 并返回一个默认值(或空响应)而不是完全失败.您也不需要 .map 只是为了转换,您可以使用通用函数.来源:Angular.io - 获取错误详细信息.

所以,一个通用的 .get() 方法应该是这样的:

import { Injectable } from '@angular/core';从@angular/common/http"导入 { HttpClient, HttpErrorResponse };从 'rxjs/Observable' 导入 { Observable };导入 'rxjs/add/operator/catch';导入 'rxjs/add/observable/of';导入 'rxjs/add/observable/empty';导入 'rxjs/add/operator/retry';//不要忘记导入@Injectable()导出类数据服务{baseUrl = 'http://localhost';构造函数(私有httpClient:HttpClient){}//注意<T>,使方法通用get<T>(url, params): Observable<T>{返回 this.httpClient.get<T>(this.baseUrl + url, {params}).retry(3)//可选地添加重试.catch((err: HttpErrorResponse) => {if (err.error instanceof Error) {//发生客户端或网络错误.相应地处理它.console.error('发生错误:', err.error.message);} 别的 {//后端返回一个不成功的响应码.//响应正文可能包含有关出错的线索,console.error(`后端返回代码 ${err.status}, body was: ${err.error}`);}//...可选地返回一个默认的回退值,以便应用程序可以继续(选择一个)//这可能是一个默认值//return Observable.of({my: "default value..."});//或者只是一个空的 observable返回 Observable.empty();});}}

处理错误将使您的应用程序继续运行,即使 URL 处的服务状况不佳.

当您想向每个方法返回特定的默认响应时,这种针对每个请求的解决方案非常有用.但如果您只关心错误显示(或有全局默认响应),更好的解决方案是使用拦截器,如下所述.

运行在此处运行演示 plunker.>


高级用法:拦截所有请求或响应

再一次,Angular.io 指南显示:

<块引用>

@angular/common/http 的一个主要特性是拦截,能够声明位于应用程序和后端之间的拦截器.当您的应用程序发出请求时,拦截器在将其发送到服务器之前对其进行转换,并且拦截器可以在您的应用程序看到它之前在返回的途中转换响应.这对于从身份验证到日志记录的所有事情都很有用.

当然,它可以用于以非常简单的方式处理错误(演示plunker在这里):

import { Injectable } from '@angular/core';导入 { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse,来自 '@angular/common/http' 的 HttpErrorResponse };从 'rxjs/Observable' 导入 { Observable };导入 'rxjs/add/operator/catch';导入 'rxjs/add/observable/of';导入 'rxjs/add/observable/empty';导入 'rxjs/add/operator/retry';//不要忘记导入@Injectable()导出类 HttpErrorInterceptor 实现 HttpInterceptor {拦截(请求:HttpRequest,下一个:HttpHandler):Observable>{返回 next.handle(request).catch((err: HttpErrorResponse) => {if (err.error instanceof Error) {//发生客户端或网络错误.相应地处理它.console.error('发生错误:', err.error.message);} 别的 {//后端返回一个不成功的响应码.//响应正文可能包含有关出错的线索,console.error(`后端返回代码 ${err.status}, body was: ${err.error}`);}//...可选地返回一个默认的回退值,以便应用程序可以继续(选择一个)//这可能是一个默认值(这里必须是一个 HttpResponse)//return Observable.of(new HttpResponse({body: [{name: "Default value..."}]}));//或者只是一个空的 observablereturn Observable.empty>();});}}

提供拦截器:简单地声明上面的 HttpErrorInterceptor 不会导致您的应用使用它.您需要将其连接到您的应用模块,将其作为拦截器提供,如下:

import { NgModule } from '@angular/core';从@angular/common/http"导入 { HTTP_INTERCEPTORS };从 './path/http-error.interceptor' 导入 { HttpErrorInterceptor };@NgModule({...提供者:[{提供:HTTP_INTERCEPTORS,useClass: HttpErrorInterceptor,多:真实,}],...})导出类 AppModule {}

注意:如果你同时有一个错误拦截器和一些本地错误处理,自然地,很可能不会触发任何本地错误处理,因为错误将始终由拦截器到达本地错误处理之前进行处理.

运行在此处运行演示 plunker.>

I have a data service that looks like this:

@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)
    }
}

If I get an HTTP error (i.e. 404), I get a nasty console message: ERROR Error: Uncaught (in promise): [object Object] from core.es5.js How do I handle it in my case?

解决方案

You have some options, depending on your needs. If you want to handle errors on a per-request basis, add a catch to your request. If you want to add a global solution, use HttpInterceptor.

Open here the working demo plunker for the solutions below.

tl;dr

In the simplest case, you'll just need to add a .catch() or a .subscribe(), like:

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)
      );

But there are more details to this, see below.


Method (local) solution: log error and return fallback response

If you need to handle errors in only one place, you can use catch and return a default value (or empty response) instead of failing completely. You also don't need the .map just to cast, you can use a generic function. Source: Angular.io - Getting Error Details.

So, a generic .get() method, would be like:

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>();
          });
     }
}

Handling the error will allow you app to continue even when the service at the URL is in bad condition.

This per-request solution is good mostly when you want to return a specific default response to each method. But if you only care about error displaying (or have a global default response), the better solution is to use an interceptor, as described below.

Run the working demo plunker here.


Advanced usage: Intercepting all requests or responses

Once again, Angular.io guide shows:

A major feature of @angular/common/http is interception, the ability to declare interceptors which sit in between your application and the backend. When your application makes a request, interceptors transform it before sending it to the server, and the interceptors can transform the response on its way back before your application sees it. This is useful for everything from authentication to logging.

Which, of course, can be used to handle errors in a very simple way (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>>();
      });
  }
}

Providing your interceptor: Simply declaring the HttpErrorInterceptor above doesn't cause your app to use it. You need to wire it up in your app module by providing it as an interceptor, as follows:

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 {}

Note: If you have both an error interceptor and some local error handling, naturally, it is likely that no local error handling will ever be triggered, since the error will always be handled by the interceptor before it reaches the local error handling.

Run the working demo plunker here.

这篇关于在 Angular HttpClient 中捕获错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆