RestTemplate异常后无法自定义异常处理器

RestTemplate异常后无法自定义异常处理器

背景:在开发一些数据采集工具的时候使用了RestTemplate进行数据采集,使用过程中发现调用第三方API返回非200状态码的时候会报错,处理方法有两种,第一种:使用自定义异常处理器进行处理,第二种:在catch异常模块对异常使用instanceof判断异常类型,然后获取异常信息。

1. 问题复现

1. 准备API接口

这个API接口返回两种状态码,一种式200的,一种式400的,我们看下默认的RestTemplate如何处理

@RestController
public class APIController {

    @GetMapping("/api/200")
    public ResponseEntity testAPI200(){
        JSONObject entries = new JSONObject();
        entries.append("code","200");
        entries.append("msg","请求成功");
        return  new ResponseEntity(entries,HttpStatus.OK);

    }

    @GetMapping("/api/400")
    public ResponseEntity  testAPI400(){
        JSONObject entries = new JSONObject();
        entries.append("code","400");
        entries.append("msg","请求失败");
        return  new ResponseEntity(entries,HttpStatus.BAD_REQUEST);
    }
}

使用postman调用效果:
image.png

2.准备RestTemplate环境

由于这个模块非我开发的,别人开发好的,因此我这里不对别人代码做过多的更改,因此就没使用异常处理器的方式进行处理,采用自己catch异常后判断异常类型
HttpHelperUtil:

public class HttpHelperUtil {
    
    public static String request(String url, HttpMethod Methods, String RequestParams, Map<String,String> customerHeaders)   {

        RestTemplate restTemplate = new RestTemplate();
        HttpEntity<String> Entity = new HttpEntity<>(RequestParams,null);
        ResponseEntity<String> response = null;
        try {
            response = restTemplate.exchange(url, Methods, Entity, String.class);
        } catch (Exception e) {
            e.printStackTrace();
//            请求异常后返回异常状态
            return null;
        }
        if (response.getStatusCode().equals(HttpStatus.OK)) {
            return response.getBody();
        }
        return response.getBody();
    }
}

测试代码:

@SpringBootTest
class DemoApplicationTests {
    @Autowired
    private RestTemplate restTemplate;

    @Test
    void contextLoads() {
        ResponseEntity<String> response = null;
        try {
            response = restTemplate.exchange("http://localhost:8080/api/200", HttpMethod.GET, null, String.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("=====状态码:" + response.getStatusCode());
        System.out.println("=====响应体:" + response.getBody());
    }

}

使用RestTemplate调用测试状态码为200的效果:
image.png
使用RestTemplate调用测试状态码为400的效果,很明显是作为异常输出了:
image.png
然后看下使用地方的写法,这种写法有个问题就是当API返回的不是200的状态码时,就会报异常,按照正常逻辑,400,500的状态码第三方系统返回的也是正常的数据,需要在业务逻辑里判断这种失败的数据,不应该在调用的工具类里判断,**此时如果在catch里获取response的数据会拿到null,**所以这个地方会有问题。

2.原因分析

如果直接这样写就会使用默认的异常处理器,来看下默认的异常处理器是如何处理异常的。
DefaultResponseErrorHandler:

public class DefaultResponseErrorHandler implements ResponseErrorHandler {
    public DefaultResponseErrorHandler() {
    }

    // 通过response判断是否异常
    public boolean hasError(ClientHttpResponse response) throws IOException {
        int rawStatusCode = response.getRawStatusCode();
        HttpStatus statusCode = HttpStatus.resolve(rawStatusCode);
        return statusCode != null ? this.hasError(statusCode) : this.hasError(rawStatusCode);
    }

    // 通过statusCode 状态码判断是否异常
    protected boolean hasError(HttpStatus statusCode) {
        return statusCode.isError();
    }

    // 未知状态码判断
    protected boolean hasError(int unknownStatusCode) {
        Series series = Series.resolve(unknownStatusCode);
        return series == Series.CLIENT_ERROR || series == Series.SERVER_ERROR;
    }

    // 处理异常请求
    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);
        }
    }

    // 获取异常返回体
    private String getErrorMessage(int rawStatusCode, String statusText, @Nullable byte[] responseBody, @Nullable Charset charset) {
        String preface = rawStatusCode + " " + statusText + ": ";
        if (ObjectUtils.isEmpty(responseBody)) {
            return preface + "[no body]";
        } else {
            charset = charset != null ? charset : StandardCharsets.UTF_8;
            String bodyText = new String(responseBody, charset);
            bodyText = LogFormatUtils.formatValue(bodyText, -1, true);
            return preface + bodyText;
        }
    }

    // 处理异常请求
    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()) {
        case CLIENT_ERROR:
            throw HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset);
        case SERVER_ERROR:
            throw HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset);
        default:
            throw new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, charset);
        }
    }

    /** @deprecated */
    
    @Deprecated
    protected HttpStatus getHttpStatusCode(ClientHttpResponse response) throws IOException {
        HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
        if (statusCode == null) {
            throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
        } else {
            return statusCode;
        }
    }

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

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

其实从上面看到异常信息输出了,只不过不是按照我们的要求输出的,而是在异常中输出的,那么我们有两种方法处理这种问题。

1. 使用自定义异常处理器

这里我写个API,返回400的编码,然后使用异常处理器的方式处理:

public class CustomErrorHandler implements ResponseErrorHandler {
    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        if (response.getStatusCode() == HttpStatus.OK){
            return false;
        }
        return true;
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {

    }
}

image.png

3. 在Catch中处理

既然能够在异常中打印那么我们也可以在异常中获取到这部分数据

@SpringBootTest
class DemoApplicationTests {
    @Autowired
    private RestTemplate restTemplate;

    @Test
    void contextLoads() {
        ResponseEntity<String> response = null;
        try {
            response = restTemplate.exchange("http://localhost:8080/api/400", HttpMethod.GET, null, String.class);
        } catch (Exception e) {
            e.printStackTrace();
            String errorCode = null;
            String errorBody = null;
            if (e instanceof HttpStatusCodeException) {
                HttpStatusCodeException httpStatusCodeException = (HttpStatusCodeException) e;
                errorCode = httpStatusCodeException.getStatusCode().value() + "";
                errorBody = httpStatusCodeException.getResponseBodyAsString();
            }
            System.out.println("=====状态码:" + errorCode);
            System.out.println("=====响应体:" + errorBody);
        }
    }
}

执行结果可以看到,能够正常输出了:
image.png

4. 总结

在实际使用过程中更建议使用异常处理器进行处理。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

4935同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值