自定義resttemplate的ErrorHandler

 

結論:

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);
	}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章