使用RestTemplate 请求,接口返回400+或500+不抛出异常的方法

使用RestTemplate请求其他服务的时候,经常会遇到返回401,404,400,或者由于业务逻辑报500等情况,这种时候RestTemplate会报异常RestClientException,所以代码里面就必须catch异常再继续走逻辑,非常麻烦。

        这种情况多了就想让他不报错,因为返回的ResponseEntity中已经有了返回码,返回头和返回体的全部信息,可以自己处理这些信息。下面开始介绍解决办法。

        从RestTemplate的请求方法入手,走查代码exchange→execute→doExecute,发现返回值在请求后,会通过handleResponse()判断是否有错误,判断和处理交由ResponseErrorHandler对象处理异常。

        现在思路就清晰了,只要将restTemplate的ResponseErrorHandler定制一下就可以。

        解决了基本问题后又发现一个问题。由于我们的返回信息在正常情况和异常情况下结构是不同的,即RestTemplate<T>中的T可能会变成其他结构,甚至一个errorMsg,这时候需要特殊处理。继续看代码,在handleResponse()之后,response交由一个接口ResponseExtractor<T> 负责抽取信息,查看该接口的实现,由于需要返回ResponseEntity,找到内部实现类ResponseEntityResponseExtractor,发现该类是HttpMessageConverterExtractor的一个代理类,这个被代理的抽取器进入extractData方法。方法里主要就是从默认的HttpMessageConverter中挑选一个,然后对返回的数据流进行解析。

        我们的问题是已经声明了泛型为我们需要的类型,那message这种字符串怎么放呢?这里我暂时的办法是就放到body中,因为我也不知道放哪好。由于我们声明了泛型,就不能简单的放到body里了,因为我们知道Java的泛型并不是真正的类型,只是引用类型的编译和擦除实现,所以就通过强转、擦除的方式将无法转换的body直接以字符串形式放到body中。

        具体代码:

        1.errorHandler在处理异常时不处理,替换默认的抛出异常操作

public class NoErrorResultErrorHandler extends DefaultResponseErrorHandler {

    @Override
 public void handleError(ClientHttpResponse response) {
    }
}

        2.自定义返回的ResponseEntity,擦除返回值类型

public class EraseTypeResponseEntity extends ResponseEntity {

    private String msg;

 EraseTypeResponseEntity(String msg, MultiValueMap<String, String> headers, HttpStatus status) {
        super(null, headers, status);
 this.msg = msg;
 }

    @Override
 public Object getBody() {
        return this.msg;
 }
}

        3.自定义数据抽取器,如果body正常解析不成功,就使用自定义的返回Entity,把返回值直接以String形式放到body中

public class NoExceptionRestTemplateResponseEntityExtractor<T> implements ResponseExtractor<ResponseEntity<T>> {

    private static final Logger log = LoggerFactory.getLogger(NoExceptionRestTemplateResponseEntityExtractor.class);
 private final HttpMessageConverterExtractor<T> delegate;

 NoExceptionRestTemplateResponseEntityExtractor(Type responseType, List<HttpMessageConverter<?>> converters) {
        if (responseType != null && Void.class != responseType) {
            this.delegate = new HttpMessageConverterExtractor<>(responseType, converters);
 } else {
            this.delegate = null;
 }
    }

    @Override
 public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException {
        if (this.delegate != null) {
            T body = null;
 try {
                body = this.delegate.extractData(response);
 } catch (RuntimeException e) {
                String content;
 try {
                    content = StreamUtils.copyToString(response.getBody(), UTF_8);
 log.error("请求接口时返回[" + content + "],无法解析返回值", e);
 } catch (Exception exception) {
                    content = "";
 log.error("请求接口时返回未知结果,无法解析返回值", exception);
 }
                return new EraseTypeResponseEntity(content, response.getHeaders(), response.getStatusCode());
 }
            return new ResponseEntity<>(body, response.getHeaders(), response.getStatusCode());
 } else {
            return new ResponseEntity<>(response.getHeaders(), response.getStatusCode());
 }
    }
}

        4. 创建RestTemplate代理,使用自定义的收据抽取器

public class RestTemplateProxy extends RestTemplate {

    @Override
 protected <T> ResponseExtractor<ResponseEntity<T>> responseEntityExtractor(Type responseType) {
        return new NoExceptionRestTemplateResponseEntityExtractor<>(responseType, getMessageConverters());
 }

    @Override
 protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor) throws RestClientException {
        logger.info("发起请求" + method.toString() + " " + url.toString());
 T t = super.doExecute(url, method, requestCallback, responseExtractor);
 logger.info("请求" + method.toString() + " " + url.toString() + "返回:" + BeanToStringUtil.stringBean(t));
 return t;
 }
}

        5. 使用自定义的代理Template取代默认注入的RestTemplate

@Bean
public RestTemplate restTemplate(ObjectMapper objectMapper) {
    RestTemplate restTemplate = new RestTemplateProxy();
 restTemplate.setErrorHandler(new NoErrorResultErrorHandler());
 return restTemplate;
}

        6.使用的时候不需要再try-catch,可以通过返回值判断接口数据是否正常,例如:

HttpEntity requestEntity = new HttpEntity(cloudosService.getHttpHeadersWithToken(certainGroupIdBO.getCloudIp()));
ResponseEntity<ActionPageWrapperDTO> responseEntity = restTemplate.exchange(builder.build(),
 HttpMethod.GET,
 requestEntity,
 ActionPageWrapperDTO.class,
 certainGroupIdBO.getTenantId(),
 certainGroupIdBO.getGroupId());
if (HttpStatus.OK == responseEntity.getStatusCode()) {
    return responseEntity.getBody();
} else if (HttpStatus.NOT_FOUND == responseEntity.getStatusCode()) {
    ActionPageWrapperDTO response = new ActionPageWrapperDTO();
 response.setRecords(Lists.newArrayList());
 response.setTotal(0);
 response.setPageSize(pageSize);
 response.setPageNo(pageNo);
 return response;
} else {
    logger.error("查询伸缩事件列表SERV-异常-actionId:{},param:{},返回结果:[{}]", actionId, BeanToStringUtil.stringBean(certainGroupIdBO), BeanToStringUtil.stringBean(responseEntity));
 throw new ActionNetworkException("查询伸缩事件列表异常");
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值