HttpClient 拦截器强制请求重复

HttpClient Interceptor forces request duplicates

我喜欢有一个 HttpInterceptor (Angular 6),它添加授权 Headers 但也处理 401 以重定向到登录页面。 这是我的代码:

import {Injectable} from '@angular/core';
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable} from 'rxjs';
import {Router} from '@angular/router';

@Injectable()
export class JwtInterceptor implements HttpInterceptor {


    constructor(private router: Router) {
    }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // add authorization header with jwt token if available
        let currentUser = JSON.parse(localStorage.getItem('currentUser'));
        if (currentUser && currentUser.token) {
            request = request.clone({
                setHeaders: {
                    Authorization: `Bearer ${currentUser.token}`,
                },
            });
        }

        const req = next.handle(request);

        // ---------------- VVV ---------------
        req.subscribe(() => {
        }, (error: any) => {
            if (error instanceof HttpErrorResponse && (error as HttpErrorResponse).status === 401)
                this.router.navigate(['public/login']);
        });
        // ---------------- ^^^ ---------------

        return req;
    }
}

除 vvv / ^^^ 注释中的代码强制发送两次请求外,一切正常。为什么这个?好的,我可能必须根据请求进行订阅,因为这个拦截器订阅了并且可能是我使用 HttpClient 的服务。 有没有更好的方法来解决这个问题?

编辑:以下是来自 package.json 的依赖项:

...
"@angular/compiler": "6.0.3",
"@angular/core": "6.0.3",
"@angular/http": "6.0.3",
"@angular/router": "6.0.3",
"rxjs": "^6.2.0",
"rxjs-compat": "^6.2.0",
"rxjs-tslint": "^0.1.4",
"zone.js": "^0.8.26"
...

您应该使用 do() 而不是 subscribe()

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { 
   // add authorization header with jwt token if available
        let currentUser = JSON.parse(localStorage.getItem('currentUser'));
        if (currentUser && currentUser.token) {
            request = request.clone({
                setHeaders: {
                    Authorization: `Bearer ${currentUser.token}`,
                },
            });
        }
  return next.handle(request).do((event: HttpEvent<any>) => {
    if (event instanceof HttpResponse) {
      // do stuff with response if you want
    }
  }, (err: any) => {
    if (err instanceof HttpErrorResponse {
      if (err.status === 401) {
        this.router.navigate(['public/login']);
      }
    }
  });
}

Difference between do() and subscribe()

编辑

导入do运算符import { do } from 'rxjs/operators';

为什么 do/tap 在用作订阅时被忽略?

这里的重点是 do() 不会影响流的流动,这与其他运算符不同。它接受响应,做一些事情,即使它修改了响应,流也会忽略它。当您尝试将它用作 subscribe() 时,它会被忽略,因为您已经在下面的语句

中返回了流

安装 rxjs-compat 并试试这个,它对我有用。

import { Injectable } from '@angular/core';
import {
  HttpInterceptor,
  HttpHandler,
  HttpRequest,
  HttpEvent,
  HttpResponse,
  HttpErrorResponse
} from '@angular/common/http';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { do } from 'rxjs/operators';

@Injectable()
export class InterceptorService implements HttpInterceptor {
  constructor(private router: Router) {}

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    if (
      localStorage.getItem('JWT-TOKE') !== '' && 
      localStorage.getItem('JWT-TOKE') !== null
    ) {
      const JWT = localStorage.getItem('JWT-TOKE');
      req = req.clone({
        setHeaders: {
          Authorization: 'Bearer ' + JWT
        }
      });
    }
    return next.handle(req).do(
      (event: HttpEvent<any>) => {},
      (err: any) => {
        if (err instanceof HttpErrorResponse) {
          if (err.status === 401) {
            localStorage.clear();
            this.router.navigate(['/login']);
          }
        }
      }
    );
  }
}

更新

如果你不使用 rxjs-compat 则使用 tap 而不是 do,因为 do 是 [=26= 中的保留关键字].

import { Injectable } from "@angular/core";
import {
    HttpInterceptor,
    HttpHandler,
    HttpRequest,
    HttpEvent,
    HttpResponse,
    HttpErrorResponse
} from "@angular/common/http";
import { Router } from "@angular/router";
import { Observable } from "rxjs";
import { tap } from "rxjs/operators";
import "rxjs/add/operator/do";

@Injectable()
export class HttpInterceptorService implements HttpInterceptor {
    constructor(private router: Router) {}


    intercept(
        req: HttpRequest<any>,
        next: HttpHandler
    ): Observable<HttpEvent<any>> {
        if (
            localStorage.getItem("jwtToken") !== "" &&
            localStorage.getItem("jwtToken") !== null
        ) {
            const JWT = localStorage.getItem("jwtToken");
            req = req.clone({
                setHeaders: {
                    Authorization: JWT
                }
            });
        }

        return next.handle(req).pipe(
            tap((event: HttpEvent<any>) => {
                console.log(event);
                // handle error here
            }),
            tap((err: any) => {

                console.log(err);
            })
        );

    }
}