結論:
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);
}