本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看 活动链接
复盘
A 系统需要调用 B 系统的某个接口,在程序里我是使用的 RestTemplate 进行的远程调用,调用之后给我响应报文为 RestTemplateMessage 。然后再使用了 postman 工具调用 B 系统的接口,响应之后给我响应报文为 postmanMessage。
- RestTemplateMessage
{
"code": 400,
"msg": "Bad Request",
"desc": "系统异常,请求稍后再试",
"data":null
}
- postmanMessage
{
"code": 400,
"message": "xxx问题",
"result": null
}
我当时就懵了,调用同一个接口,返回不同的响应?
然后我注意到使用
postman 工具请求的时候,Status 状态也是 400 的时候,脑海里立马想到是不是因为 RestTemplate 框架对 400 这个 Status的状态码,有什么特殊的处理。
这里使用的是 restTemplate.exchange()方法。调试进入this.execute方法 ---》this.doExecute()方法。doExecute方法里有一行代码为this.handleResponse(url, method, response);,进入 handleResponse方法,可以看到有一行 errorHandler.handleError(url, method, response);
handleRrroer这个方法就是用来处理失败情况的。默认会使用 DefaultResponseErrorHandler类的实现方法。
调试
public void handleError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
if (statusCode == null) {
byte[] body = this.getResponseBody(response);
String message = this.getErrorMessage(response.getRawStatusCode(), response.getStatusText(), body, this.getCharset(response));
throw new UnknownHttpStatusCodeException(message, response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), body, this.getCharset(response));
} else {
// 看这个
this.handleError(response, statusCode);
}
}
protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
String statusText = response.getStatusText();
HttpHeaders headers = response.getHeaders();
byte[] body = this.getResponseBody(response);
Charset charset = this.getCharset(response);
String message = this.getErrorMessage(statusCode.value(), statusText, body, charset);
switch(statusCode.series()) {
//客户端异常 400、401、403、404、405...
case CLIENT_ERROR:
throw HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset);
//服务端异常 500、501、502、503、504...
case SERVER_ERROR:
throw HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset);
default:
throw new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, charset);
}
}
看到这已经很明显了,如果状态码满足 RespTemplate 所规定的,就会被拦截,RespTemplate 对其抛出异常,但这个异常会将真正的响应消息吞掉。那这肯定不行,对于问题的排查不太友好。那怎么解决呢!
看到这里,其实我们已经明白在使用 RestTemplate的时候,当你的 call 没有成功返回 200 的时候,比如返回400、500之类的,RestTemplate里面有一个DefaultResponseErrorHandler,它会自动拦截住这些httpstatus为400、500的 响应然后给你抛出一个异常。
处理方案
继承 DefaultResponseErrorHandler类,重写handleError方法
@Slf4j
public class CustomizeErrorHandle extends DefaultResponseErrorHandler {
/**
* RespTemplate 里有一个 DefaultResponseErrorHandler,会进行拦截 400、401、402...500等响应code,拦截之后给你抛出一个异常
* 但异常中并没有具体的响应信息,使用需自定义一个 ErrorHandle
* 具体源码:
HttpClientErrorException.create:客户端异常处理
HttpServerErrorException.create:服务端异常处理
**/
@Override
public void handleError(ClientHttpResponse response) throws IOException {
// 响应码
HttpStatus statusCode = response.getStatusCode();
log.info("statusCode = {}", JSON.toJSONString(statusCode));
int value = statusCode.value();
// 响应内容
InputStream body = response.getBody();
//将流转换为String
String result = IOConverter.IOConverterToStr(body);
//如果状态码为 400 则抛出自定义Exception或者是Exception
if(value == HttpStatus.BAD_REQUEST.value()){
log.info("result:{}",result);
throw new Exception(result);
}else {
//不符合,走父类逻辑
super.handleError(response);
}
}
}
在需要使用的地方赋值
restTemplate.setErrorHandler(new CustomizeErrorHandle());