使用RestTemplate遇到的坑 | Java Debug 笔记

2,300 阅读2分钟

本文正在参加「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
}

我当时就懵了,调用同一个接口,返回不同的响应? image.png 然后我注意到使用 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());