如何处理HttpClient请求的日志记录?

时间:2018-02-20 20:16:47

标签: angular logging rxjs error-logging angular-httpclient

我正在使用Angular和rxjs 5.x.x.对于每个http请求,我必须将响应记录在一个集中的位置 - 如果成功或失败。

我面临的问题是在调用catch条款的情况下。

例如:

  callRemote() {
    return this.http.get("http://aaa.dom")
                    .catch(f=>Rx.Observable.of(f))
                    .do(f => console.log("logging response both bad and ok..."))
  }

这是http请求失败。但我必须调用do运算符,因为我需要记录它。 但是,如果调用.catch,则不会调用.do。这就是我这样做的原因:

  .catch(f=>Rx.Observable.of(f))

所以我基本上捕获错误并将其包装为新的observable并继续.do函数。

到目前为止一切顺利。

现在的问题是,最终,在订阅时,我想知道请求是否失败或确定。

基本上这就是我订阅的方式:

 this._http.callRemote().subscribe(
      (x => console.log('OK')),
      (x => console.log('ERROR')),
      (() => console.log('COMPLETE'))
    );

问题是所有内容都被重定向到sucess回调函数。甚至失败。我明白为什么会这样 - 因为我把catch作为新的Observables失败了。

问题

包装错误是否可能会转移到error回调,而不会转移到success回调?

Online demo

1 个答案:

答案 0 :(得分:7)

正如评论中已经提到的,捕获不应该首先吞下错误,这肯定是nogo。通过这样做,你只是阻止rxjs链中的错误信号。

最终你可以在捕获中进行日志记录然后重新抛出,但这样只有日志副作用才有点过分。

最简单的选择是在do运算符中使用错误回调 arguent。

根据this tutorial page,do运算符可以使用3个参数(回调fns),它基本上与订阅签名匹配:

1s参数:下一个回调

第二个参数:错误回调

第三个参数:完成回调

所以你可以重构以下内容:

callRemote() {
    return this.http.get("http://aaa.dom")
                    .do(
                      response => console.log("logging response both bad and ok..."), 
                      error => console.log("Something exploded, call 911");
  }

基本上,您可以将该操作附加到基本代码中的每个HttpClient调用。很整洁,没有?

等等!乍一看,这看起来可能很整洁,但在你想要的同一时刻它会适得其反:

  • 修改日志记录行为
  • 以任何方式重构

为什么?

您将基本上使用do()操作对每个可能的后端调用进行修补。如果对该逻辑进行1次更改意味着在3个以上的位置更改代码,则会发生一些臭味。

更好的方法

随着 HttpClient 的推出,添加了另一个API:HttpInterceptor API。

基本上,您可以在一个点上拦截所有传出的请求。

如何?如下:

第一步,创建一个可用于封装日志记录逻辑的可注入服务;

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor
} from '@angular/common/http';
import { AuthService } from './auth/auth.service';
import { Observable } from 'rxjs/Observable';
import { tap } from 'rxjs/operators'; // fancy pipe-able operators

@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
       tap(
           response => console.log("Oh boy we got an answer"), 
           error => console.log("Something might be burning back there")
       ));
  }
}

第二步,通过令牌提供角度来了解LoggingInterceptor的存在:

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgModule, ClassProvider } from '@angular/core';

import { LoggingInterceptor } from '../some-place';

const LOGGING_INTERCEPTOR_PROVIDER: ClassProvider = {
   provide: HTTP_INTERCEPTORS ,
   useClass: LoggingInterceptor,
   multi: true
};

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

就是这样!现在,您可以以真正集中的方式记录所有传出请求,并在必要时使用这些日志执行其他一些很酷的操作。