springboot中RestTemplate远程调用:设置连接超时、自定义异常处理

目录

设置连接超时

调用示例--get请求

调用示例--post请求(接口使用@RequestParam注解即以表单方式提交)

调用示例--post请求(接口使用@RequestBody注解即以json方式提交)

自定义异常处理

1、异常现象

2、源码解析-默认实现

2.1、从HttpResponse解析出Http StatusCode,如果状态码StatusCode为null,就抛出UnknownHttpStatusCodeException异常。

2.2、如果StatusCode存在,则解析出StatusCode的series,也就是状态码段(除了2xx段,其他全是异常状态码),解析规则是StatusCode/100取整。

2.3、进一步针对客户端异常和服务端异常进行处理,处理的方法是抛出HttpClientErrorException。也就是第一小节截图中出现的异常的原因,下面三个异常都是RestClientException的子类

3、RestTemplate自定义异常处理

参考文章


设置连接超时

SimpleClientHttpRequestFactory.class中默认无限制

private int connectTimeout = -1;

private int readTimeout = -1;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {
  @Bean
  public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
    RestTemplate restTemplate = new RestTemplate(factory);
    return restTemplate;
  }

  @Bean
  public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
    SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
    factory.setReadTimeout(5000);
    factory.setConnectTimeout(10000);
    return factory;
  }
}

调用示例--get请求

@Autowired
private RestTemplate restTemplate;
Map paramMap = new HashMap<String, String>(1);
paramMap.put("userId", "system");
try {
ResponseEntity<JSONArray> forEntity = restTemplate.getForEntity("http://192.168.3.213:32410/queryUserRoles?userId={userId}", JSONArray.class, paramMap);
JSONArray routeDTOs = forEntity.getBody();
System.out.println("========================="+routeDTOs.toJSONString());
} catch (RestClientException e) {
  //RestClientException是HttpClientErrorException、HttpServerErrorException等的父类
  e.printStackTrace();
}

调用示例--post请求(接口使用@RequestParam注解即以表单方式提交

调用示例--post请求(接口使用@RequestBody注解即以json方式提交

//发送Post数据并解析数据
String casServiceUrl = casClientProperties.getCasServiceUrl();
String url = casServiceUrl + "/addClient";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<String>(serviceInfo.toJSONString(), headers);
try {
  ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity(url, request, JSONObject.class);
  JSONObject body = responseEntity.getBody();
  if (!body.isEmpty() && body.getInteger("code") != null) {
    int code = body.getInteger("code").intValue();
    if (code == 200) {
      log.info("CAS client added successfully, serviceInfo:\n{}",serviceInfo.toJSONString());   
    } else if (code == 201) {
      ......
    }

自定义异常处理

1、异常现象

在使用RestTemplate进行远程接口服务调用的时候,当请求的服务出现异常:超时、服务不存在等情况的时候(响应状态非200、而是400、500等HTTP状态码),就会抛出如下异常:

2、源码解析-默认实现

RestTemplate请求结果异常是可以自定义处理的。在开始进行自定义的异常处理逻辑之前,我们有必要看一下异常处理的默认实现。

ResponseErrorHandler是RestTemplate请求结果的异常处理器接口
接口的第一个方法hasError用于判断HttpResponse是否是异常响应(通过状态码)
接口的第二个方法handleError用于处理异常响应结果(非2xx状态码段)
DefaultResponseErrorHandlerResponseErrorHandler的默认实现,所以我们就来看看DefaultResponseErrorHandler是如何来处理异常响应的?

2.1、从HttpResponse解析出Http StatusCode,如果状态码StatusCode为null,就抛出UnknownHttpStatusCodeException异常。

2.2、如果StatusCode存在,则解析出StatusCode的series,也就是状态码段(除了2xx段,其他全是异常状态码),解析规则是StatusCode/100取整。

public enum Series {

   INFORMATIONAL(1),  // 1xx/100
   SUCCESSFUL(2),  // 2xx/100
   REDIRECTION(3), // 3xx/100
   CLIENT_ERROR(4), // 4xx/100   ,客户端异常
   SERVER_ERROR(5); // 5xx/100 ,服务端异常

   ......
   ............

}

2.3、进一步针对客户端异常和服务端异常进行处理,处理的方法是抛出HttpClientErrorException。也就是第一小节截图中出现的异常的原因,下面三个异常都是RestClientException的子类

3、RestTemplate自定义异常处理

所以我们要实现自定义异常,实现ResponseErrorHandler接口 [仿照DefaultResponseErrorHandler实现]就可以。

import com.genertech.plm.aia.login.cas.customerAuthentication.exception.MyFailedLoginException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.UnknownHttpStatusCodeException;

import java.io.IOException;
import java.nio.charset.Charset;

/**
 * @author: huo.qw
 * @createTime: 2023/06/27 下午 07:27
 * @description: 自定义RestTemplate抛出的异常为CAS的异常,
 * 解决RestTemplate抛出的异常提前被CAS源码PolicyBasedAuthenticationManager捕获的问题
 * @version: v1.0
 */
@Slf4j
public class CustomerRestTemplateResponseErrorHandler implements ResponseErrorHandler {
  public CustomerRestTemplateResponseErrorHandler() {
  }

  /**
   * 判断返回结果response是否是异常结果
   * 主要是去检查response 的HTTP Status
   * [仿照DefaultResponseErrorHandler实现即可]
   */
  public boolean hasError(ClientHttpResponse response) throws IOException {
    int rawStatusCode = response.getRawStatusCode();
    // 模拟失败
    //rawStatusCode = 400;
    HttpStatus[] var3 = HttpStatus.values();
    int var4 = var3.length;

    for (int var5 = 0; var5 < var4; ++var5) {
      HttpStatus statusCode = var3[var5];
      if (statusCode.value() == rawStatusCode) {
        return this.hasError(statusCode);
      }
    }

    return this.hasError(rawStatusCode);
  }

  protected boolean hasError(HttpStatus statusCode) {
    return statusCode.is4xxClientError() || statusCode.is5xxServerError();
  }

  protected boolean hasError(int unknownStatusCode) {
    int seriesCode = unknownStatusCode / 100;
    return seriesCode == HttpStatus.Series.CLIENT_ERROR.value() || seriesCode == HttpStatus.Series.SERVER_ERROR.value();
  }

  /*//默认的
  @Override
  public void handleError(ClientHttpResponse response) throws IOException {
    HttpStatus statusCode = this.getHttpStatusCode(response);
    switch(statusCode.series()) {
      case CLIENT_ERROR:
        throw new HttpClientErrorException(statusCode, response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
      case SERVER_ERROR:
        throw new HttpServerErrorException(statusCode, response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
      default:
        throw new UnknownHttpStatusCodeException(statusCode.value(), response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
    }
  }*/

  //改造的
  @Override
  public void handleError(ClientHttpResponse response) throws IOException {
    HttpStatus statusCode = this.getHttpStatusCode(response);
    switch (statusCode.series()) {//HttpStatus.Series.CLIENT_ERROR
      case CLIENT_ERROR:
        HttpClientErrorException httpClientErrorException = new HttpClientErrorException(statusCode, response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
        log.error("RestTemplate RPC CLIENT_ERROR:\n", httpClientErrorException);
        // 抛出自定义的可以返回给前端信息的异常
        throw new MyFailedLoginException("RPC CLIENT_ERROR");
      case SERVER_ERROR:
        HttpServerErrorException httpServerErrorException = new HttpServerErrorException(statusCode, response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
        log.error("RestTemplate RPC SERVER_ERROR:\n", httpServerErrorException);
        // 抛出自定义的可以返回给前端信息的异常
        throw new MyFailedLoginException("RPC SERVER_ERROR");
      default:
        UnknownHttpStatusCodeException e = new UnknownHttpStatusCodeException(statusCode.value(), response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
        log.error("RestTemplate RPC UnknownHttpStatusCodeException:\n", e);
        // 抛出自定义的可以返回给前端信息的异常
        throw new MyFailedLoginException("RPC UnknownHttpStatusCode");
    }
  }

  protected HttpStatus getHttpStatusCode(ClientHttpResponse response) throws IOException {
    try {
      return response.getStatusCode();
    } catch (IllegalArgumentException var3) {
      throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
    }
  }

  protected byte[] getResponseBody(ClientHttpResponse response) {
    try {
      return FileCopyUtils.copyToByteArray(response.getBody());
    } catch (IOException var3) {
      return new byte[0];
    }
  }

  protected Charset getCharset(ClientHttpResponse response) {
    HttpHeaders headers = response.getHeaders();
    MediaType contentType = headers.getContentType();
    return contentType != null ? contentType.getCharset() : null;
  }
}

将MyRestErrorHandler 在RestTemplate实例化的时候进行注册即可

参考文章

精讲RestTemplate第7篇-自定义请求失败异常处理_51CTO博客_resttemplate请求超时处理

springboot中RestTemplate的用法_springboot resttemplate用法-CSDN博客

  • 18
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值