0%

angular2 拦截器

@angular/common/http的主要特性之一是拦截器。可以声明一些拦截器,拦在应用和后端之间。当应用发起一个请求时,拦截器可以在请求被发往服务器之前转换这个请求。并且在应用看到服务器发回来的响应之前,转换这个响应。这对于处理包括认证和记录日志在内的一系列工作都非常有用。

要实现一个拦截器,就需要声明一个实现了HttpInterceptor接口的类,它只有一个intercept()方法。我们创建一个拦截器:

1
2
3
4
5
6
7
8
9
10
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import {Observable} from 'rxjs/Observable';

@Injectable()
export class NoopIntercedptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req);
}
}

这个拦截器只是简单的转发请求而不做任何修改。

intercept方法把一个请求对象转换成一个返回这个响应的可观察对象(ObserveAble),大多数情况,拦截器会对请求做一些小修改,然后才把它转给拦截器链中的其他部分。也就是所传进来的next参数。next是一个HttpHandle,是一个类似于intercept的接口,它会把一个请求对象转换为一个可观察的响应对象。在拦截器中,next总是代表位于拦截器链中的下一个拦截器(如果有的话),如果没有更多的拦截器,它就会使最终的一个,所以大多数拦截器的最后一句都会以他们转换后请求对象为参数调用next.handle函数。

拦截器的三个特性

1.事件

interceptHttpHandle.handle返回的可观察对象是Observable<HttpEvent<any>>而不是Observable<HttpResponse<any>>。这是因为拦截器的工作层级低于HttpClient接口。单个请求会发生多个事件,比如上传和下载过程事件。

拦截器必须透传所有它不能理解或不打算修改的事件。它不能过滤自己不准备处理的事件。

2.顺序

当我们在一个应用中提供了多个拦截器时,angular会按照你提供时的顺序来应用它们(即模块的providers数组中列出的顺序)。

3.不可变性

拦截器要检查和修改准备发出的请求和接收进来的响应。但是,HttpRequestHttpResponse类在很大程度上是不可变的。因为一旦修改了,应用很可能会重发请求。而拦截器很可能会多次处理同一请求。如果请求是可变的,那么每次重试时都可能和原始的请求不一致。而不可变对象可以确保拦截器每次重试时处理的都是同一个请求。

如果确定要修改请求体,我们就得自己复制它,修改这个副本,并使用这个新的请求。

1
2
3
4
5
6
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let dupReq = req.clone({url: req.url.replace('http://', 'https://')});
return next.handle(dupReq).do(event => {
console.log(event);
});
}

我们使用拦截器将url中的协议改为https。

是时候展现真正的技术了

我们来构造一个拦截器,当请求的结果是特定的code表示用户未登录的时候,自动跳转到登录界面。

我们的拦截器 api.interceptor.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Injectable } from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { Router } from '@angular/router';
import { tap } from 'rxjs/operators';
@Injectable()

export class ApiInterceptor implements HttpInterceptor {

constructor(private router: Router) {}

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(tap(event => {
// event为响应内容
if (event instanceof HttpResponse) {
// 判断响应体内容中的登录状态
if (event.body.code === 1000) {
// 未登录,跳转到登录页面
this.router.navigate(['/login']);
}
}
}));
}
}

RxJS 的 tap 操作符会捕获请求成功了还是失败了。

应用拦截器

我们只需要在app.module.ts中导入拦截器就可以了。

1
2
3
4
5
6
7
8
providers: [
ApiUntilService,
{
provide: HTTP_INTERCEPTORS,
useClass: ApiInterceptor,
multi: true,
}
],

这样,在每个应用中就会应用拦截器了,当服务端接口返回code为1000时,就说明未登录,页面跳转到登录页面。

码字辛苦,打赏个咖啡☕️可好?💘