本文是精講響應(yīng)式WebClient第5篇,前篇的blog訪問地址如下:
- 精講響應(yīng)式webclient第1篇-響應(yīng)式非阻塞IO與基礎(chǔ)用法
- 精講響應(yīng)式WebClient第2篇-GET請求阻塞與非阻塞調(diào)用方法詳解
- 精講響應(yīng)式WebClient第3篇-POST、DELETE、PUT方法使用
- 精講響應(yīng)式WebClient第4篇-文件上傳與下載
本文來為大家介紹一下,當(dāng)WebClient請求發(fā)生異常的時候,該如何處理。為了講解異常處理,我們需要先制造出異常,所以我們先為大家介紹:請求超時時長的設(shè)置。
一、請求超時時長的設(shè)置
要想模擬超時異常,我們首先要知道超時時長的正常配置渠道是怎么樣的。如下文代碼所示:
-
ChannelOption.CONNECT_TIMEOUT_MILLIS用來設(shè)置連接超時時長,單位是毫秒 -
ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS)用來設(shè)置讀數(shù)據(jù)超時時長,單位是毫秒 -
WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS)用來設(shè)置寫數(shù)據(jù)超時時長,單位是毫秒
//初始化一個WebClient
private WebClient getWebClient(){
TcpClient tcpClient = TcpClient
.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.doOnConnected(connection -> {
connection.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS));
connection.addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS));
});
return WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))
.build();
}
當(dāng)我們把連接超時時長設(shè)置為5(毫秒)的時候,則連接肯定會超時。隨便發(fā)送一個請求,超時之后會拋出ConnectTimeoutException

當(dāng)我們把讀數(shù)據(jù)超市時長設(shè)置為5(毫秒)的時候,則數(shù)據(jù)讀操作肯定會超時。隨便發(fā)送一個請求,超時之后會拋出ReadTimeoutException

二、處理特定的異常
下面我們就以ConnectTimeoutException為例,進(jìn)行異常處理
//制造異常,將超時時間設(shè)置為5毫秒
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5)
然后執(zhí)行下面的GET請求,上文WebClient的baseurl為:"http://jsonplaceholder.typicode.com" ,該網(wǎng)站是一個免費(fèi)提供HTTP服務(wù)端測試的網(wǎng)站。
@Test
public void testSimple() throws Exception {
Mono<String> mono = getWebClient()
.get() // 發(fā)送GET 請求
.uri("/posts/1") //服務(wù)請求路徑,基于baseurl
.retrieve() // 獲取響應(yīng)體
.bodyToMono(String.class) //響應(yīng)數(shù)據(jù)類型轉(zhuǎn)換
//進(jìn)行異常處理
.doOnError(ConnectTimeoutException.class, err -> {
System.out.println("發(fā)生錯誤:" +err.getMessage() );
});
System.out.println(mono.block());
}
上文中的doOnError是我們本節(jié)為大家介紹的異常處理方法,用于處理ConnectTimeoutException,輸出結(jié)果如下:

從輸出結(jié)果上看:一:異常得到處理,因?yàn)榭吹搅薙ystem.out打印日志。二是異常仍然被拋出了,沒有得到返回值。
三、請求異常給出默認(rèn)返回值
從第二小節(jié)中的代碼及控制臺輸出,可以看出HTTP 客戶端請求沒有得到返回值,而是繼續(xù)把異常對外拋出。假如我們目前的需求是,不論請求成功失敗,都給客戶端一個返回值,該怎么做?也就是說我們需要在請求發(fā)生異常的時候,給出默認(rèn)返回值。
@Test
public void testReturn() throws Exception {
Mono<String> mono = getWebClient()
.get() // 發(fā)送GET 請求
.uri("/posts/1") //服務(wù)請求路徑,基于baseurl
.retrieve() // 獲取響應(yīng)體
.bodyToMono(String.class) //響應(yīng)數(shù)據(jù)類型轉(zhuǎn)換
.doOnError(ConnectTimeoutException.class, err -> {
System.out.println("發(fā)生錯誤:" +err.getMessage() );
})
.onErrorReturn("請求發(fā)生異常,請檢查!");
System.out.println(mono.block());
}
使用onErrorReturn();給出請求的默認(rèn)返回值,輸出結(jié)果如下:

可以看到請求測試用例成功pass了,因?yàn)槲覀兘o出了異常處理的默認(rèn)返回值,沒有把異常繼續(xù)拋出。
四、分類異常處理
上面的異常處理方法,只能處理指定的某種異常:ConnectTimeoutException。如果說我們想讓異常處理相對通用一些該怎么辦?有的小伙伴可能會想到攔截異常的父類Exception,當(dāng)然這也是一種辦法。
.doOnError(Exception.class, err -> {
System.out.println("發(fā)生錯誤:" +err.getMessage() );
});
我們下面為大家介紹一種,針對HTTP 響應(yīng)異常處理更友好的一種方式。通常來說,異??梢苑譃閮煞N:
- 一種是客戶端輸入或訪問異常,比如:訪問的資源不存在404,沒有權(quán)限訪問資源403,輸入的數(shù)據(jù)不符合格式等等。這種異常通常是用戶訪問了不該訪問的資源,或者輸入了不該輸入的數(shù)據(jù)導(dǎo)致的。通常用HTTP狀態(tài)碼表示在400-499范圍內(nèi)。
- 另一種是服務(wù)端內(nèi)部錯誤,比如:500服務(wù)內(nèi)部錯誤、502網(wǎng)關(guān)錯誤等等。這種異常通常和用戶沒什么關(guān)系,是IT基礎(chǔ)設(shè)施或者編程導(dǎo)致的異常。
所以我們只需要針對上面的兩類異常進(jìn)行處理即可。如下文代碼所示:
- e.is4xxClientError()表示的是400-499狀態(tài)碼段的異常
- e.is5xxClientError()表示的是500-599狀態(tài)碼段的異常
public void testSimple2() throws Exception {
Mono<String> mono = getWebClient()
.get() // 發(fā)送GET 請求
.uri("/postss/1") //服務(wù)請求路徑,基于baseurl
.retrieve() // 獲取響應(yīng)體
.onStatus(e -> e.is4xxClientError(), resp -> {
System.out.println("發(fā)生客戶端輸入錯誤:" + resp.statusCode().value() + " "
+ resp.statusCode().getReasonPhrase());
return Mono.error(new RuntimeException("請求失敗"));
})
.onStatus(e -> e.is5xxServerError(), resp -> {
System.out.println("發(fā)生服務(wù)端錯誤:" + resp.statusCode().value() + " "
+ resp.statusCode().getReasonPhrase());
return Mono.error(new RuntimeException("服務(wù)器異常"));
})
.bodyToMono(String.class); //響應(yīng)數(shù)據(jù)類型轉(zhuǎn)換
System.out.println(mono.block());
}
現(xiàn)在我們將請求地址由正確的"/posts/1",改成錯誤的"/postss/1",所以當(dāng)我們訪問服務(wù)端的時候,服務(wù)端并不存在這個資源。異常處理的輸出結(jié)果如下:

歡迎關(guān)注我的博客,里面有很多精品合集
- 本文轉(zhuǎn)載注明出處(必須帶連接,不能只轉(zhuǎn)文字):字母哥博客。
覺得對您有幫助的話,幫我點(diǎn)贊、分享!您的支持是我不竭的創(chuàng)作動力! 。另外,筆者最近一段時間輸出了如下的精品內(nèi)容,期待您的關(guān)注。