HttpInterceptor - 希望服务器响应在某些错误情况下流向我的组件

HttpInterceptor - want server responses to flow to my component for certain error conditions

我希望响应正文中的错误响应从我的 API 服务器一直流到我的组件以获得某些错误代码,例如 400 或 422。它在没有 HttpInterceptor 的情况下运行良好下面的实现。

function  handleHttpError() {
  return (response: any) => {
    let newResponse;
    if (response.status === 0) {
      // Error connecting to the server
      newResponse = { error: 'Error connecting to the server!' }
    } else {
      // Server returns a response in response.error
      newResponse = response.error;
    }
    return of(newResponse);
  };
}

export class MyService {
  ...

  saveTransaction(trans: Transaction): Observable<APIResponseBody> {
    return this.http.post<APIResponseBody>(this.transUrl, trans)
      .pipe(catchError(handleHttpError()));
  }

  ...
}

export class MyComponent {

  ...

  onSubmit() {
    this.myService.saveTransaction(this.data) {
      .subscribe((res: APIResponseBody) => {
        if (res.error) {
          this.toastService.showError(`${res.error}: ${JSON.stringify(res.attributes)}`);
        } else if (res.trans) {
          this.trans = res.trans;
        }
      })
    }
  }
  ...

}

但是,当我将错误处理移至 HttpInterceptor 时,它不起作用。在以下实现中,所有非 2xx http 错误似乎都被拦截器过滤掉了:

export class HttpInterceptorService implements HttpInterceptor {
  constructor() { }
  intercept (request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .pipe(
        catchError((response: any) => {
          let newResponse;
          if (response.status === 0) {
            // Error connecting to the server
            newResponse = { error: 'Error connecting to the server!' }
          } else {
            // Server returns a response in response.error
            newResponse = response.error;
          }
          return of(newResponse);
        })
      );
  }
  }
}
  
export class MyService {
  ...
  
  saveTransaction(trans: Transaction): Observable<APIResponseBody> {
    return this.http.post<APIResponseBody>(this.transUrl, trans);
  }
  
  ...
}

我的拦截器实现中缺少什么?

我认为这主要是因为没有在您的处理程序函数中返回正确的类型,您返回的是未知类型的可观察对象,这不是拦截器所期望的。看看这个并尝试一下:

@Injectable()
export class ErrorHandlerInterceptor implements HttpInterceptor {

  intercept(request: HttpRequest < any > , next: HttpHandler): Observable < HttpEvent < any >> {
    return next.handle(request).pipe(catchError((error) => this.errorHandler(error)));
  }

  // Customize the default error handler here if needed
  private errorHandler(response: HttpErrorResponse): Observable < never > {
    if (!environment.production) {
      // Do something with the error
      this.logger.logError('Request error ' + JSON.stringify(response));
    }

    // console.error(error);
    const httpErrorCode = response.status;
    switch (httpErrorCode) {
      case 404:
        this.router.navigateByUrl('/not-found');
        break;
      case 403:
        this.router.navigateByUrl('/forbidden');
        break;
      case 500:
      default:
        // 500 Error Handing
        break;
    }

    throw response;
  }
}

拦截器需要 return 一个 Observable<HttpEvent>,在你的例子中是 Observable<HttpResponse>

因此,无需直接 returning newResponse 对象,您只需创建一个 new HttpResponse 并将您的响应设置为响应的主体。

export class HttpInterceptorService implements HttpInterceptor {
  constructor() { }
  intercept (request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .pipe(
        catchError((response: any) => {
          ...

          return of(new HttpResponse({ body: newResponse }));

        })
    );
  }
}

干杯

你在这里提到了 2 个例子,所以我会尝试解释每个例子中发生了什么。

没有拦截器的第一个例子

我发现您使用 catchError 函数的方式存在问题。

saveTransaction(trans: Transaction): Observable<APIResponseBody> {
    return this.http.post<APIResponseBody>(this.transUrl, trans)
      .pipe(catchError(handleHttpError()));  <-------------------------
  }

在 catch 错误中,您要么传递一个箭头函数 () => { } 说明它的行为方式,要么传递对您案例中现有函数的引用 handleHttpError.

所以正确的方法是

saveTransaction(trans: Transaction): Observable<APIResponseBody> {
        return this.http.post<APIResponseBody>(this.transUrl, trans)
          .pipe(catchError(handleHttpError));  <-------------------------
      }

handleHttpError() 的方式是直接执行 handleHttpError 方法,然后将调用的响应传递给 catchError。另外,您声明的函数 handleHttpError 没有采用任何错误的参数。

所以第一个没有拦截器的例子是错误的,只是巧合,你看到的与你期望的相符。

拦截器的第二个例子

我们来看第二个拦截器的例子。您在 catchError 中传递的箭头函数没有正确实现,因此它不起作用。

尝试以下

catchError((response: HttpErrorResponse) => {
      let newResponse;
      if (response.error instanceof ErrorEvent) {
           newResponse = { error: 'Error connecting to the server!' }
      } else {
          //The response object here holds 2 information
          // response.error  -> information about error
          // response.status -> information about the status code returned
          newResponse = response;
      }
      return of(newResponse);
    });