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);
})
);
}
}
我喜欢有一个 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);
})
);
}
}