HttpInterceptor 拦截器 - 网络请求超时与重试的简单实现
...
拦截器
在Angular
项目中其实有着十分重要的地位,拦截器可以统一对 HTTP 请求进行拦截处理,我们可以在每个请求体或者响应后对应的流添加一系列动作或者处理数据,再返回给使用者调用。
每个 API 调用的时候都不可避免的会出现网络超时的情况,但是这种情况是多变的,可能是网络问题,也有可能是服务端问题,尽管如此,我们也只需对网络超时
这一种情况来进行处理。
套壳
按照惯例写一个拦截器的壳
import {HttpInterceptor,HttpRequest,HttpHandler,HttpEvent
} from '@angular/common/http'
import { Injectable } from '@angular/core'
import { Observable } from 'rxjs'
import { timeout } from 'rxjs/operators'/** 拦截器 - 超时以及重试设置 */
@Injectable()
export class TimeoutInterceptor implements HttpInterceptor {constructor() { }intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {return next.handle(req)}
}
加入超时处理
超时
rxjs
确实功能强大,这里的超时我们只需要使用timeout
操作符便可以实现。这里的超时处理逻辑是挂到next.handle()
返回的可观察对象中。
next 对象表示拦截器链表中的下一个拦截器。 这个链表中的最后一个 next 对象就是 HttpClient 的后端处理器(backend handler),它会把请求发给服务器,并接收服务器的响应。
大多数的拦截器都会调用 next.handle(),以便这个请求流能走到下一个拦截器,并最终传给后端处理器。
先在类外部定义一个超时时限
/** 超时时间 */
const DEFAULTTIMEOUT = 8000
在拦截器主函数handle
流中加入操作符
return next.handle(req).pipe(timeout(DEFAULTTIMEOUT)
)
其实这样就实现了超时拦截器,当超过设定的时间还没有响应数据的时候,handle流
便会在抛出相应的超时错误。
捕获超时
在超时错误发生后,我们可能需要第一时间捕获到以便给用户一个提示。这里可以直接使用catchError
操作符。
在拦截器主函数handle流中加入操作符
return next.handle(req).pipe(//... 已有的代码忽略catchError((err: HttpErrorResponse) => {this.nzNotificationService.error('网络超时','请重试')return throwError(err)})
)
handle
需要返回一个可观察对象,所以我们顺便把捕获的错误返回。这样一来,便可以在捕获到超时的时候显示一个简单的提示。
超时重试
一般来说,超时出现的情况是不确定的,即使多了提示,有些请求用户也没有其他的动作去重试,只能刷新页面,那此时重新请求就显得重要了,我们可以在捕获到超时请求之后对这个请求再进行固定次数的重试,避免某些情况的超时影响用户体验。
对流进行多次重试,可以使用retryWhen
操作符。
retryWhen
操作符接受一个函数作为参数,这个函数会接受一个由一组错误组成的Observable
,我们可以针对这个Observable
做一些节奏控制来促动重试动作,然后在函数中返回这个可观察对象。
一个简单的retryWhen
组成:
retryWhen(err$ => {return err$.pipe(//一些节奏控制...)
})
如此以来,我们就可以直接使用此操作符来实现了。
添加retryWhen
重试
我们在next.handle
流挂上retryWhen
操作符
return next.handle(req).pipe(//... 已有的代码忽略retryWhen(err$ => {return err$})
)
其实此时就已经实现了重试机制,但是运行结果你会发现,当超时错误永远存在时,重试的次数是无限的,也就是程序会不断得请求,因为我们还没有做任何的节奏控制。
那么,我们就需要先确定一下重试的节奏,比如最大的重试次数、每次延迟多久重试、重试上限次数还是失败了的处理等等。那就简单处理提到的这3个情况吧。
重试最大次数
既然retryWhen
中err$
是一个错误组成的流,那么每一次超时重试失败后,err$
便会推动一次数据,我们可以使用scan
操作符来累计获取重试失败的次数,以此来控制重试的最大次数。
scan
操作符接受两个参数,第一个是累加函数,可以在函数中获取上一次scan
的累加值以及所在流的数据,第二个值接受一个scan
的初始累加值,所以可以很轻松地获取重试错误的次数。
在拦截器类外部定义一个最大重试次数:
/** 最大重试次数 */
const MAXRETRYCOUNT = 3
我们在retryWhen
中挂上scan
操作符
return next.handle(req).pipe(//... 已有的代码忽略retryWhen(err$ => {return err$.pipe(scan((errCount, err) => {if (errCount >= MAXRETRYCOUNT) {throw err}return errCount + 1}, 0))})
)
在scan
中,我们获取了累加值(errCount
,初始为0 ),判断是否大于上限,如果大于便直接抛出超时错误(err
),如果小于便返回累加值 +1。至此,拦截器只会再重试到最大次数还是失败的情况下抛出超时错误。
延迟重试
重试最好加上延迟,避免某些场景下一定请求错误的情况,比如服务器的某些请求过滤。延迟十分简单,只需要在err$
挂上delay
操作符,流的推动便会以一定的间隔实行。
return next.handle(req).pipe(//... 已有的代码忽略retryWhen(err$ => {return err$.pipe(//... 已有的代码忽略delay(1000))})
)
重试的友好提示
可能有的时候网络太慢,或者重试次数设置得比较大,这样在请求重试的时候会耗时比较久,而用户是不知道此时正在重试的,所以加一个友好的提示可以增加用户体验。
而添加提示是属于比较透明或者说属于副作用动作,此时我们可以直接使用tap
操作符来进行操作。由于是挂到scan
之后,所以在tap
中获取到的就是重试的累加值。
return next.handle(req).pipe(//... 已有的代码忽略retryWhen(err$ => {return err$.pipe(//... 已有的代码忽略tap(errCount => {if(errCount == 1){//第一次重试时显示友好信息this.nzNotificationService.info('网络超时','正在重新请求中...')}}))})
)
这样当第一次重新请求时,我们便给出明确的提示。
修改捕获错误(catchError
)的顺序
前面我们在没有重试功能之前设置了捕获错误,并给出提示。由于后面加了重试功能,故捕获错误的操作需要挂到重试之后,这样一来,才可以在全部重试完成后仍然失败的情况下提示用户,而不是每次重试都给出捕获到的错误提示。
return next.handle(req).pipe(timeout( ... ),retryWhen( ... ),catchError( ... )
)
完成上述步骤,一个简单的网络请求超时与重试的拦截器便实现了。完整的代码如下:
import {HttpInterceptor,HttpRequest,HttpHandler,HttpEvent,HttpErrorResponse
} from '@angular/common/http'
import { Injectable } from '@angular/core'
import { Observable, throwError
} from 'rxjs'
import { timeout, delay, retryWhen, scan, tap, catchError
} from 'rxjs/operators'
import { NzNotificationService } from 'ng-zorro-antd'/** 超时时间 */
const DEFAULTTIMEOUT = 8
/** 最大重试次数 */
const MAXRETRYCOUNT = 3//拦截器 - 超时以及重试设置
@Injectable()
export class TimeoutInterceptor implements HttpInterceptor {constructor(private nzNotificationService:NzNotificationService) { }intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {return next.handle(req).pipe(timeout(DEFAULTTIMEOUT),retryWhen(err$ => {//重试 节奏控制器return err$.pipe(scan((errCount, err) => {if (errCount >= MAXRETRYCOUNT) {throw err}return errCount + 1}, 0),delay(1000),tap(errCount => {//副作用if(errCount == 1){//第一次重试时显示友好信息this.nzNotificationService.info('网络超时','正在重新请求中...')}}))}),catchError((err: HttpErrorResponse) => {this.nzNotificationService.error('网络超时','请重试')return throwError(err)}))}
}
详细拦截器说明请前往官网文档:拦截请求和响应
HttpInterceptor 拦截器 - 网络请求超时与重试的简单实现相关推荐
- app显示服务器连接超时,APP网络请求超时反馈设计与思考
最近我负责了一个网络请求超时的反馈设计,借此机会我也顺便通过此文记录了一下整个思考过程,整理一下自己的思路. 当我们在使用APP的时候,偶尔会碰到网络状态不好的情况.那么对于网络状态不好的情况有哪些分 ...
- 拦截器获取请求参数post_SpringBoot拦截器如何获取http请求参数
1.1.获取http请求参数是一种刚需 我想有的小伙伴肯定有过获取http请求的需要,比如想 前置获取参数,统计请求数据 做服务的接口签名校验 敏感接口监控日志 敏感接口防重复提交 等等各式各样的场景 ...
- 计算机网络中请求超时是什么意思,网络请求超时怎么解决
我们知道不少朋友在上网的时候,会遇到网络请求超时的情况,那造成网络请求超时的原因是什么呢?网络请求超时就是在程序默认的等待时间内没有得到服务器的响应.跟着小编一起来看看请求超时解决方法. 网络请求超时 ...
- 简单快速处理网络请求超时的方法
最近在调试联网操作的时候碰到网络请求超时的问题,以下是简单快速处理网络超时的方法: 使用android提供的工具类AsyncTask类,此类提供了一个AsyncTask.execute().get(t ...
- Vue中使用axios的响应拦截器处理请求失败的情况(处理token过期问题)以及 登录成功跳转回原来页面问题
参考axios官方文档 // 响应拦截器 // Add a response interceptor request.interceptors.response.use(// 在2xx范围内的任何状态 ...
- vue拦截器及请求封装
1.vue项目的src文件夹中创建request文件夹 (1)request文件夹中创建index.js (拦截器主要代码) /*** 请求封装*/import axios from 'axios'; ...
- 拦截器获取请求参数post_「SpringBoot WEB 系列」RestTemplate 之自定义请求头
[WEB 系列]RestTemplate 之自定义请求头 上一篇介绍了 RestTemplate 的基本使用姿势,在文末提出了一些扩展的高级使用姿势,本篇将主要集中在如何携带自定义的请求头,如设置 U ...
- springmvc拦截器对请求参数解密_SpringMVC拦截器如何修改请求参数
拦截器1,基本拦截器: package cn.ijava.interceptor; import javax.servlet.http.HttpServletRequest; import javax ...
- axios拦截器_请求拦截器_响应拦截器---axios工作笔记010
然后我们再去看看axios的,请求拦截器,和响应拦截器. 先说一下这个,拦截器的原理,其实就是 我们发送一个请求,这个请求在发出去之前,我们的请求拦截器,先去拦截一下,拦截的时候可以对请求数据做一些处 ...
最新文章
- Arcgis Server 默认服务端口号修改方法
- 以用户体验为导向的设计表现
- Eclipse显示内存占用
- .NET WebSocket 核心原理初体验
- selenium autoit java_Java+Selenium——AutoIt工具处理文件上传
- JS特效代码大全(十一)超炫的js图片展示效果(三)
- 10亿级!淘宝大规模图像检索引擎算法设计概览
- linux 手动控制cpu转速,Linux 手动计算CPU使用率
- 纵坐标范围_探索频率范围与频响的奥秘
- LeetCode ——24. 两两交换链表中的节点
- IDEA 2021 的 debug 是怎么实现?出于这个好奇心,我越挖越深。。。
- Markdown和Latex语法
- jquery点击事件写法
- error code
- 【翠花学Vue】每日打卡——vue打卡1
- Python入门学习笔记——12.文件操作
- 函数平移口诀_呼和浩特市||初中函数巧平移方法
- Bq769XX IIC 通讯 ALERT引脚
- 详细教程||基于51单片机开发的十路抢答器设计
- 10mbps 局域网组播_无线局域网Apple Bonjour部署指南