我有一个数据服务,看起来像这样:

@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),我会收到一条令人讨厌的控制台消息: 从core.es5.js增加到ERROR Error: Uncaught (in promise): [object Object] 在我的情况下我该怎么处理呢?

推荐答案

根据你的需要,你有一些 Select .如果您想按每个请求处理错误,请在请求中添加catch.如果要添加全局解决方案,请使用HttpInterceptor.

打开here the working demo plunker获取以下解决方案.

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

但关于这一点还有更多细节,请参见下面的内容.


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

如果只需要在一个地方处理错误,可以使用catch并返回默认值(或空响应),而不是完全失败.你也不需要.map来强制转换,你可以使用一个通用函数.资料来源:Angular.io - Getting Error Details.

所以,一个通用的.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.


Advanced usage: Intercepting all requests or responses

再一次,Angular.io guide个节目:

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

当然,可以用一种非常简单的方法来处理错误(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:简单地声明上面的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 {}

Note:如果你有both个错误拦截器和一些本地错误处理,很自然地,很可能不会触发任何本地错误处理,因为错误总是由拦截器before处理,然后到达本地错误处理.

working demo plunker here.

Angular相关问答推荐

我希望在下一次有Angular 的HTTPS呼叫之前完成一次

基于另一个控制值显示值的Angular 数组

Angular将文件作为字符串读取给dxf解析器

NG-Zorro Datepicker 手动输入掩码不起作用

为什么初始化值为 1 的BehaviorSubject 不能分配给类型number?

获取 Angular 中所有子集合 firestore 的列表

SignalR:协商有效,但消息未发送(.NET/Angular)

在switchMap操作符中使用条件语句以进行第一次或随后的执行

如何根据响应变化更改 Angular Carousel 的cellsToShow

如何将模块导入独立指令

清除Angular2中的输入文本字段

Angular ngFor 索引从 1 开始 循环

Observable.forkJoin 和数组参数

Angular 5 Mat-grid 列表响应式

Angular 2 - 什么是Root Scope?

无法绑定到matDatepicker,因为它不是 input的已知属性

导航到同一条route路由不刷新组件?

在Angular 4中以'yyyy-MM-dd'格式获取当前日期

'Observable' 类型上不存在属性 'filter'

升级到 angular-6.x 会给出Uncaught ReferenceError: global is not defined