摘要:對象表示攔截器鏈表中的下一個攔截器。至此,攔截器只會再重試到最大次數還是失敗的情況下拋出超時錯誤。完成上述步驟,一個簡單的網絡請求超時與重試的攔截器便實現了。
...
攔截器在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加入超時處理 超時, next: HttpHandler): Observable > { 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, next: HttpHandler): Observable > { 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) }) ) } }
詳細攔截器說明請前往官網文檔:攔截請求和響應
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98649.html
摘要:包含一些狀態來決定是重試還是中止,但是這個狀態位于堆棧上,不需要將它存儲在全局的任何位置,因此我們將此稱為無狀態重試。將拋出原始異常,除非在有狀態的情況下,當沒有可用的恢復,在這種情況下,它將拋出。 spring-retry 該項目為Spring應用程序提供聲明式重試支持,它用于Spring Batch、Spring Integration、Apache Hadoop的Spring(以...
摘要:在這篇文章中,我們描述了我們如何在里設計重試,使能夠在最小化風險的同時,自動提高系統可靠性。配置重試的最常用方法,是指定在放棄之前執行的最大重試次數。超時時,將取消請求并返回響應。但是在上面的服務配置文件中,我們將在服務器端指定重試政策。 showImg(https://segmentfault.com/img/bVbo113?w=4400&h=1007);作者:Alex Leong ...
摘要:的類圖如下主要根據創建擴展了,創建攔截的,這里會設置攔截器,這是集成的核心,當發起請求調用的時候,會先經過攔截器,然后才真正發起請求。和是配合使用的,最大重試次數是針對每一個的,如果設置,這樣觸發最大重試次數就是次。 上一篇文章我們分析了ribbon的核心原理,接下來我們來看看springcloud是如何集成ribbon的,不同的springcloud的組件(feign,zuul,Re...
摘要:對于異步的請求,使用的是異步客戶端即。要實現的配置設計以及使用舉例要實現的配置設計以及使用舉例首先,我們要實現的,其包含三個重試重試的要在負載均衡之前,因為重試的時候,我們會從負載均衡器獲取另一個實例進行重試,而不是在同一個實例上重試多次。 本系列代碼地址:https://github.com/JoJoTec/spring-cloud-parent 為何需要封裝異步 HT...
摘要:簽發的用戶認證超時刷新策略這個模塊分離至項目權限管理系統與前后端分離實踐,感覺那樣太長了找不到重點,分離出來要好點。這樣在有效期過后的時間段內可以申請刷新。 簽發的用戶認證token超時刷新策略 這個模塊分離至項目api權限管理系統與前后端分離實踐,感覺那樣太長了找不到重點,分離出來要好點。 對于登錄的用戶簽發其對應的jwt,我們在jwt設置他的固定有效期時間,在有效期內用戶攜帶jw...
閱讀 1263·2021-11-23 09:51
閱讀 2638·2021-09-03 10:47
閱讀 2234·2019-08-30 15:53
閱讀 2414·2019-08-30 15:44
閱讀 1375·2019-08-30 15:44
閱讀 1194·2019-08-30 10:57
閱讀 1924·2019-08-29 12:25
閱讀 1087·2019-08-26 11:57