结论:
resttemplate有默认的ErrorHandler,resttemplate发起http请求,一旦server抛异常,默认的只能打印status,无法打印message,自定义ErrorHandler,能打印status和message
server
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
int i = 1 / 0;
return "hello world";
}
}
client
postman
用postman请求,返回如下,能解析异常信息,"message": "/ by zero",
restTemplate
但为啥restTemplate请求只能看到状态码,看不到详细信息呢,光看到500肯定是不够的,原因是resttemplate没有自定义ErrorHandler
2019-12-14 12:42:07.207 ERROR [hyg,,,] 10144 --- [ main] c.h.d.r.ResponseErrorHandlerTest : 请求失败,url http://localhost:8000/hello
org.springframework.web.client.HttpServerErrorException$InternalServerError: 500 null
at org.springframework.web.client.HttpServerErrorException.create(HttpServerErrorException.java:79) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
restTemplate code
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ResponseErrorHandlerTest {
@Autowired
private RestTemplate restTemplate;
@Test
public void responseErrorHandlerTest() {
String url = "http://localhost:8000/hello";
try {
String hello = restTemplate.getForObject(url, String.class);
assertThat(hello).contains(hello);
} catch (Exception e) {
log.error("请求失败,url {}", url, e);
}
}
}
自定义ErrorHandler能打印出来
自定义ErrorHandler,是不是能看到了
2019-12-14 12:50:20.302 ERROR [hyg,,,] 8044 --- [ main] c.h.d.r.ResponseErrorHandlerTest : 请求HelloException失败,异常信息 {"timestamp":"2019-12-14 12:50:20","status":500,"error":"Internal Server Error","message":"/ by zero","path":"/hello"}
2019-12-14 12:50:20.318 ERROR [hyg,,,] 8044 --- [ main] c.h.d.r.ResponseErrorHandlerTest : 请求失败,url http://localhost:8000/hello
com.hyg.demo.spring.resttemplate.HelloException: {"timestamp":"2019-12-14 12:50:20","status":500,"error":"Internal Server Error","message":"/ by zero","path":"/hello"}
at com.hyg.demo.resttemplate.ResponseErrorHandlerTest.responseErrorHandlerTest(ResponseErrorHandlerTest.java:37) ~[test-classes/:na]
如何自定义resttemplate的ErrorHandler
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ResponseErrorHandlerTest {
@Autowired
private RestTemplate restTemplate;
@Test
public void responseErrorHandlerTest() {
this.restTemplate = new RestTemplate();
restTemplate.setErrorHandler(responseErrorHandler(new HelloException()));
String url = "http://localhost:8000/hello";
try {
String hello = restTemplate.getForObject(url, String.class);
assertThat(hello).contains(hello);
} catch (Exception e) {
log.error("请求失败,url {}", url, e);
}
}
private ResponseErrorHandler responseErrorHandler(HelloException helloException) {
return new ResponseErrorHandler() {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return (response.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR ||
response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR);
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
String body = org.apache.commons.io.IOUtils.toString(response.getBody(), StandardCharsets.UTF_8);
log.error("请求{}失败,异常信息 {}", helloException.getClass().getSimpleName(), body);
try {
helloException.setStatus(response.getStatusCode().value());
helloException.setMessage(body);
helloException.setServerSide(response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR);
helloException.setRetryAble(response.getStatusCode().series() != HttpStatus.Series.CLIENT_ERROR);
throw helloException;
} catch (Exception e) {
helloException.setStatus(response.getStatusCode().value());
helloException.setMessage(body);
throw helloException;
}
}
};
}
}
服务端异常:一般以服务端名称+Exception命名
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString(callSuper = true)
@Builder
public class HelloException extends RuntimeException{
private int status; //http状态码
private boolean isServerSide; //是不是server异常
private boolean retryAble; // 是否重试,如果不是client_error就会重试
private String code;
private String message; //异常信息的body
private Map<String,String> detail;
}
DefaultResponseErrorHandler
resttemplate的私有成员变量
private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
DefaultResponseErrorHandlerd的handleError方法
protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
String statusText = response.getStatusText();
HttpHeaders headers = response.getHeaders();
byte[] body = getResponseBody(response);
Charset charset = getCharset(response);
switch (statusCode.series()) {
case CLIENT_ERROR:
throw HttpClientErrorException.create(statusCode, statusText, headers, body, charset);
case SERVER_ERROR: //body是详细信息,但没把body赋值给message
throw HttpServerErrorException.create(statusCode, statusText, headers, body, charset);
default:
throw new UnknownHttpStatusCodeException(statusCode.value(), statusText, headers, body, charset);
}
}
// 把statusCode.value() + " " + statusText当做maeeage,传给RestClientResponseException
org.springframework.web.client.RestClientResponseException#RestClientResponseException
super(statusCode.value() + " " + statusText, statusCode.value(), statusText,
responseHeaders, responseBody, responseCharset);
public RestClientResponseException(String message, int statusCode, String statusText,
@Nullable HttpHeaders responseHeaders, @Nullable byte[] responseBody, @Nullable Charset responseCharset) {
super(message);
this.rawStatusCode = statusCode;
this.statusText = statusText;
this.responseHeaders = responseHeaders;
this.responseBody = (responseBody != null ? responseBody : new byte[0]);
this.responseCharset = (responseCharset != null ? responseCharset.name() : null);
}