RestTemplate异常后无法自定义异常处理器
背景:在开发一些数据采集工具的时候使用了RestTemplate进行数据采集,使用过程中发现调用第三方API返回非200状态码的时候会报错,处理方法有两种,第一种:使用自定义异常处理器进行处理,第二种:在catch异常模块对异常使用instanceof判断异常类型,然后获取异常信息。
文章目录
- RestTemplate异常后无法自定义异常处理器
- 1. 问题复现
- 1. 准备API接口
- 2.准备RestTemplate环境
- 2.原因分析
- 1. 使用自定义异常处理器
- 3. 在Catch中处理
- 4. 总结
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调用效果:
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的效果:
使用RestTemplate调用测试状态码为400的效果,很明显是作为异常输出了:
然后看下使用地方的写法,这种写法有个问题就是当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 {
}
}
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);
}
}
}
执行结果可以看到,能够正常输出了:
4. 总结
在实际使用过程中更建议使用异常处理器进行处理。