使用RestTemplate请求其他服务的时候,经常会遇到返回401,404,400,或者由于业务逻辑报500等情况,这种时候RestTemplate会报异常RestClientException,所以代码里面就必须catch异常再继续走逻辑,非常麻烦。
这种情况多了就想让他不报错,因为返回的ResponseEntity中已经有了返回码,返回头和返回体的全部信息,可以自己处理这些信息。下面开始介绍解决办法。
从RestTemplate的请求方法入手,走查代码exchange→execute→doExecute,发现返回值在请求后,会通过handleResponse()判断是否有错误,判断和处理交由ResponseErrorHandler对象处理异常。
现在思路就清晰了,只要将restTemplate的ResponseErrorHandler定制一下就可以。
解决了基本问题后又发现一个问题。由于我们的返回信息在正常情况和异常情况下结构是不同的,即RestTemplate
我们的问题是已经声明了泛型为我们需要的类型,那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, MultiValueMapheaders, HttpStatus status) { super(null, headers, status); this.msg = msg; } @Override public Object getBody() { return this.msg; } }
3.自定义数据抽取器,如果body正常解析不成功,就使用自定义的返回Entity,把返回值直接以String形式放到body中
public class NoExceptionRestTemplateResponseEntityExtractorimplements ResponseExtractor > { private static final Logger log = LoggerFactory.getLogger(NoExceptionRestTemplateResponseEntityExtractor.class); private final HttpMessageConverterExtractor delegate; NoExceptionRestTemplateResponseEntityExtractor(Type responseType, List > converters) { if (responseType != null && Void.class != responseType) { this.delegate = new HttpMessageConverterExtractor<>(responseType, converters); } else { this.delegate = null; } } @Override public ResponseEntity 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 protectedResponseExtractor > responseEntityExtractor(Type responseType) { return new NoExceptionRestTemplateResponseEntityExtractor<>(responseType, getMessageConverters()); } @Override protected T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor 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())); ResponseEntityresponseEntity = 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("查询伸缩事件列表异常"); }