Angular 拦截器 - 执行异步操作
Angular Interceptor - perform async operation
我有以下拦截器,在访问令牌过期时用作重试包装器:
import { Injectable } from '@angular/core';
import {
HttpEvent,
HttpRequest,
HttpHandler,
HttpInterceptor,
HttpHeaders,
} from '@angular/common/http';
import { Observable, EMPTY, throwError } from 'rxjs';
import { catchError, switchMap, take } from 'rxjs/operators';
import { Router } from '@angular/router';
import { AuthService } from '../services';
@Injectable()
export class UnauthorizedInterceptor implements HttpInterceptor {
constructor(
private router: Router,
private readonly authService: AuthService
) {}
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((error, caught) => {
if (error.status === 401) {
const newToken = this.authService.getNewToken();
const updatedReq = req.clone({
setHeaders: {
Authorization: `Bearer ${newToken}`,
},
});
return next.handle(updatedReq);
}
return EMPTY;
})
);
}
}
按预期工作。
但是,函数 getNewToken
是一个异步函数,如果我将代码更新为
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError(async (error, caught) => {
if (error.status === 401) {
const newToken = await this.authService.getNewToken();
const updatedReq = req.clone({
setHeaders: {
Authorization: `Bearer ${newToken}`,
},
});
return next.handle(updatedReq);
}
return EMPTY;
})
);
}
当需要在 catchError
中执行异步操作时,此用例的解决方案是什么?
P.S:getNewToken
函数可以重写为 return observable 而不是 promise 如果这有帮助的话,但是我'我想不通
更新
TypeScript抛出的错误是
Type 'Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any> | Observable<...>>' is not assignable to type 'Observable<HttpEvent<any>>'.
Type 'HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any> | Observable<...>' is not assignable to type 'HttpEvent<any>'.
Type 'Observable<HttpEvent<any>>' is not assignable to type 'HttpEvent<any>'.ts(2322)
如果我执行 @ts-ignore
那么在浏览器中抛出的错误是
EmptyError: no elements in sequence
试试这个方法:
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
return this.handleUnauthorized(request, next);
} else {
return throwError(error);
}
})
);
}
以及“handleUnauthorized”方法的实现
handleUnauthorized(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
if (!this.isRefreshingToken) {
this.isRefreshingToken = true;
// Reset here so that the following requests wait until the token
// comes back from the refreshToken call.
this.authService.setToken(null);
const body = new HttpParams()
.set('grant_type', 'refresh_token')
.set('refresh_token', localStorage.getItem('refresh_token'));
return this.http.post('YOUR_URL', body.toString(), {
headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
}).pipe(flatMap((el: any) => {
if (el && el.access_token) {
req = req.clone({ setHeaders: { Authorization: 'Bearer ' + el.access_token } });
this.authService.setToken(el);
return next.handle(req);
} else {
this.authService.unAuthorize();
return throwError('refreshToken');
}
}), finalize(() => {
this.isRefreshingToken = false;
}), catchError(error => {
return throwError(error);
}));
} else {
return this.authService.tokenSubject.pipe(
filter(token => token)
, take(1)
, switchMap(token => {
req = req.clone({ setHeaders: { Authorization: 'Bearer ' + token.access_token } });
this.authService.setToken(token);
return next.handle(req);
}));
}
}
并且在 authService 内部,您应该具有此功能
tokenSubject = new Subject<any>();
.
.
.
setToken(newToken: any) {
this.tokenSubject.next(newToken)
}
getToken() {
return this.tokenSubject.asObservable();
}
希望这个方法对你有帮助,有什么问题可以在评论区问我
我有以下拦截器,在访问令牌过期时用作重试包装器:
import { Injectable } from '@angular/core';
import {
HttpEvent,
HttpRequest,
HttpHandler,
HttpInterceptor,
HttpHeaders,
} from '@angular/common/http';
import { Observable, EMPTY, throwError } from 'rxjs';
import { catchError, switchMap, take } from 'rxjs/operators';
import { Router } from '@angular/router';
import { AuthService } from '../services';
@Injectable()
export class UnauthorizedInterceptor implements HttpInterceptor {
constructor(
private router: Router,
private readonly authService: AuthService
) {}
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((error, caught) => {
if (error.status === 401) {
const newToken = this.authService.getNewToken();
const updatedReq = req.clone({
setHeaders: {
Authorization: `Bearer ${newToken}`,
},
});
return next.handle(updatedReq);
}
return EMPTY;
})
);
}
}
按预期工作。
但是,函数 getNewToken
是一个异步函数,如果我将代码更新为
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError(async (error, caught) => {
if (error.status === 401) {
const newToken = await this.authService.getNewToken();
const updatedReq = req.clone({
setHeaders: {
Authorization: `Bearer ${newToken}`,
},
});
return next.handle(updatedReq);
}
return EMPTY;
})
);
}
当需要在 catchError
中执行异步操作时,此用例的解决方案是什么?
P.S:getNewToken
函数可以重写为 return observable 而不是 promise 如果这有帮助的话,但是我'我想不通
更新
TypeScript抛出的错误是
Type 'Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any> | Observable<...>>' is not assignable to type 'Observable<HttpEvent<any>>'.
Type 'HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any> | Observable<...>' is not assignable to type 'HttpEvent<any>'.
Type 'Observable<HttpEvent<any>>' is not assignable to type 'HttpEvent<any>'.ts(2322)
如果我执行 @ts-ignore
那么在浏览器中抛出的错误是
EmptyError: no elements in sequence
试试这个方法:
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
return this.handleUnauthorized(request, next);
} else {
return throwError(error);
}
})
);
}
以及“handleUnauthorized”方法的实现
handleUnauthorized(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
if (!this.isRefreshingToken) {
this.isRefreshingToken = true;
// Reset here so that the following requests wait until the token
// comes back from the refreshToken call.
this.authService.setToken(null);
const body = new HttpParams()
.set('grant_type', 'refresh_token')
.set('refresh_token', localStorage.getItem('refresh_token'));
return this.http.post('YOUR_URL', body.toString(), {
headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
}).pipe(flatMap((el: any) => {
if (el && el.access_token) {
req = req.clone({ setHeaders: { Authorization: 'Bearer ' + el.access_token } });
this.authService.setToken(el);
return next.handle(req);
} else {
this.authService.unAuthorize();
return throwError('refreshToken');
}
}), finalize(() => {
this.isRefreshingToken = false;
}), catchError(error => {
return throwError(error);
}));
} else {
return this.authService.tokenSubject.pipe(
filter(token => token)
, take(1)
, switchMap(token => {
req = req.clone({ setHeaders: { Authorization: 'Bearer ' + token.access_token } });
this.authService.setToken(token);
return next.handle(req);
}));
}
}
并且在 authService 内部,您应该具有此功能
tokenSubject = new Subject<any>();
.
.
.
setToken(newToken: any) {
this.tokenSubject.next(newToken)
}
getToken() {
return this.tokenSubject.asObservable();
}
希望这个方法对你有帮助,有什么问题可以在评论区问我