HttpClient拦截器强制请求重复

时间:2018-06-20 08:27:48

标签: angular angular6

我喜欢拥有一个HttpInterceptor(角度6),它添加了授权标头,但也处理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"
...

2 个答案:

答案 0 :(得分:1)

您应该使用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()不会影响流的流,这与其他运算符不同。它接受响应,执行某些操作,即使它修改了响应,流也将忽略它。当您尝试将其用作subscribe()时,它会被忽略,因为您已经在下面的语句中返回了流

答案 1 :(得分:0)

安装 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 是javascript中的保留关键字。

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

    }
}