23

精讲响应式WebClient第5篇-请求超时设置与异常处理

 4 years ago
source link: https://segmentfault.com/a/1190000023733446
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文是精讲响应式WebClient第5篇,前篇的blog访问地址如下:

本文来为大家介绍一下,当WebClient请求发生异常的时候,该如何处理。为了讲解异常处理,我们需要先制造出异常,所以我们先为大家介绍:请求超时时长的设置。

一、请求超时时长的设置

要想模拟超时异常,我们首先要知道超时时长的正常配置渠道是怎么样的。如下文代码所示:

ChannelOption.CONNECT_TIMEOUT_MILLIS
ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS)
WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS)
//初始化一个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();
}

当我们把连接超时时长设置为5(毫秒)的时候,则连接肯定会超时。随便发送一个请求,超时之后会抛出ConnectTimeoutException

uiyAru3.png!mobile

当我们把读数据超市时长设置为5(毫秒)的时候,则数据读操作肯定会超时。随便发送一个请求,超时之后会抛出ReadTimeoutException

aemyIzR.png!mobile

二、处理特定的异常

下面我们就以ConnectTimeoutException为例,进行异常处理

//制造异常,将超时时间设置为5毫秒
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5)

然后执行下面的GET请求,上文WebClient的baseurl为:"http://jsonplaceholder.typicode.com" ,该网站是一个免费提供HTTP服务端测试的网站。

@Test
public void testSimple() throws Exception {
   Mono<String> mono = getWebClient()
               .get()    // 发送GET 请求
               .uri("/posts/1")  //服务请求路径,基于baseurl
               .retrieve() // 获取响应体
               .bodyToMono(String.class) //响应数据类型转换
                //进行异常处理
               .doOnError(ConnectTimeoutException.class, err -> {
                  System.out.println("发生错误:" +err.getMessage() );
               });
   System.out.println(mono.block());
}

上文中的doOnError是我们本节为大家介绍的异常处理方法,用于处理ConnectTimeoutException,输出结果如下:

FRB3am7.png!mobile

从输出结果上看:一:异常得到处理,因为看到了System.out打印日志。二是异常仍然被抛出了,没有得到返回值。

三、请求异常给出默认返回值

从第二小节中的代码及控制台输出,可以看出HTTP 客户端请求没有得到返回值,而是继续把异常对外抛出。假如我们目前的需求是,不论请求成功失败,都给客户端一个返回值,该怎么做?也就是说我们需要在请求发生异常的时候,给出默认返回值。

@Test
public void testReturn() throws Exception {
   Mono<String> mono = getWebClient()
               .get()    // 发送GET 请求
               .uri("/posts/1")  //服务请求路径,基于baseurl
               .retrieve() // 获取响应体
               .bodyToMono(String.class) //响应数据类型转换
               .doOnError(ConnectTimeoutException.class, err -> {
                  System.out.println("发生错误:" +err.getMessage() );
               })
               .onErrorReturn("请求发生异常,请检查!");
   System.out.println(mono.block());
}

使用 onErrorReturn(); 给出请求的默认返回值,输出结果如下:

Bb2AjyA.png!mobile

可以看到请求测试用例成功pass了,因为我们给出了异常处理的默认返回值,没有把异常继续抛出。

四、分类异常处理

上面的异常处理方法,只能处理指定的某种异常:ConnectTimeoutException。如果说我们想让异常处理相对通用一些该怎么办?有的小伙伴可能会想到拦截异常的父类Exception,当然这也是一种办法。

.doOnError(Exception.class, err -> {
   System.out.println("发生错误:" +err.getMessage() );
});

我们下面为大家介绍一种,针对HTTP 响应异常处理更友好的一种方式。通常来说,异常可以分为两种:

  • 一种是客户端输入或访问异常,比如:访问的资源不存在404,没有权限访问资源403,输入的数据不符合格式等等。这种异常通常是用户访问了不该访问的资源,或者输入了不该输入的数据导致的。通常用HTTP状态码表示在400-499范围内。
  • 另一种是服务端内部错误,比如:500服务内部错误、502网关错误等等。这种异常通常和用户没什么关系,是IT基础设施或者编程导致的异常。

所以我们只需要针对上面的两类异常进行处理即可。如下文代码所示:

  • e.is4xxClientError()表示的是400-499状态码段的异常
  • e.is5xxClientError()表示的是500-599状态码段的异常
public void testSimple2() throws Exception {
   Mono<String> mono = getWebClient()
               .get()    // 发送GET 请求
               .uri("/postss/1")  //服务请求路径,基于baseurl
               .retrieve() // 获取响应体
               .onStatus(e -> e.is4xxClientError(), resp -> {
                  System.out.println("发生客户端输入错误:" + resp.statusCode().value() + " "
                              + resp.statusCode().getReasonPhrase());
                  return Mono.error(new RuntimeException("请求失败"));
               })
               .onStatus(e -> e.is5xxServerError(), resp -> {
                  System.out.println("发生服务端错误:" + resp.statusCode().value() + " "
                              + resp.statusCode().getReasonPhrase());
                  return Mono.error(new RuntimeException("服务器异常"));
               })
               .bodyToMono(String.class); //响应数据类型转换
   System.out.println(mono.block());
}

现在我们将请求地址由正确的"/posts/1",改成错误的"/postss/1",所以当我们访问服务端的时候,服务端并不存在这个资源。异常处理的输出结果如下:

q2ayIbB.png!mobile

欢迎关注我的博客,里面有很多精品合集

  • 本文转载注明出处(必须带连接,不能只转文字): 字母哥博客

觉得对您有帮助的话,帮我点赞、分享!您的支持是我不竭的创作动力!。另外,笔者最近一段时间输出了如下的精品内容,期待您的关注。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK