今天在Spring-boot项目中使用RestTemplate的时候遇到了问题。现在做一个小的总结也是给其他遇到这个问题的人给一个提示。
背景重现:用RestTemplate发送Post请求去其他微服务获取数据。其他微服务返回了4XX的错误code。这个时候RestTemplate就抛出错误,将错误打印出来了。但是我想要拿到这个错误的code进行下一步的操作。于是看了一下RestTemplate的部分代码。发现用RestTemplate作为HttpClient,你在上层没办法捕获到错误。
更改方案: 今天在google仔细寻找以后,发现有方法可以解决这个问题。如果百度搜索不到问题还是推荐上google去找吧,一般都还是可以找到解决方案的。解决方案附在后面第二部分。
我调用RestTemplate的代码如下:
RestTemplate restTemplate = restTemplateConfig.tokenRetrieveRestTemplate(); HttpEntity<MultiValueMap<String,String>> httpEntity= new HttpEntity(tokenParamUtil.getMultiValueParams(code),headers); ResponseEntity<Token> response = restTemplate .exchange(OAUTH_TOKEN_ENDPOINT,POST, httpEntity,Token.class); log.info("Visitor successfully get token by authorization code {}",code);抛出的错误如下:
org.springframework.web.client.HttpClientErrorException: 400 null at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:63) at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613) at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:531) at com.siemens.mindsphere.tokengenerator.service.TokenService.getTokenByCode(TokenService.java:68)通过抛出的异常进行查看RestTemplate的代码。
在RestTemplate的handleResponse函数里面根据Response的Code就throw了异常。而异常在RestTemplate里面就捕获了,所以使用RestTemplate那么想自己拿到ResponseCode来判断进行下一步。目前看来是不行的。所以最好的方式还是换使用其他的HttpClient。
@Override public void handleError(ClientHttpResponse response) throws IOException { //获取Response的StausCode。判断statusCode是否是4XX和5XX如果是就抛出IOException HttpStatus statusCode = getHttpStatusCode(response); switch (statusCode.series()) { case CLIENT_ERROR: throw new HttpClientErrorException(statusCode, response.getStatusText(), response.getHeaders(), getResponseBody(response), getCharset(response)); case SERVER_ERROR: throw new HttpServerErrorException(statusCode, response.getStatusText(), response.getHeaders(), getResponseBody(response), getCharset(response)); default: throw new RestClientException("Unknown status code [" + statusCode + "]"); } } public enum Series { INFORMATIONAL(1), SUCCESSFUL(2), REDIRECTION(3), CLIENT_ERROR(4), SERVER_ERROR(5); }
--------------------------------------------------------分割线---------------------------------------------------------------
解决方案:
1、自己创建一个ExceptionHandler的类,实现ResponseErrorHandler接口
public class ThrowErrorHandler implements ResponseErrorHandler{ @Override public boolean hasError(ClientHttpResponse response) throws IOException { //返回false表示不管response的status是多少都返回没有错 //这里可以自己定义那些status code你认为是可以抛Error return false; } @Override public void handleError(ClientHttpResponse response) throws IOException { //这里面可以实现你自己遇到了Error进行合理的处理 } }2、在创建RestTemplate的时候设置ErrorHandler由我们自己写的ErrorHandler
@Configuration public class RestTemplateConfig { @Bean public RestTemplate tokenRetrieveRestTemplate() { RestTemplate restTemplate=new RestTemplate(); //Response status code 4XX or 5XX to the client. restTemplate.setErrorHandler(new ThrowErrorHandler()); return restTemplate; } }
3、这样RestTemplate就会按照你自己定义的方式来判断是非要抛出Error。然后我们可以在客户端去通过Response的code来做相应的操作,比如继续往上throw Error还是将Error进行处理了。
ResponseEntity<String> response = restTemplate .exchange(OAUTH_TOKEN_ENDPOINT,POST, httpEntity,String.class); if(!response.getStatusCode().is2xxSuccessful()){ log.info("Get token code error: {}",response.getBody()); throw new RestException(response.getBody()); }附加内容:这部分单独做一个博客又感觉内容太少了,还是添加在上面做个小小的总结吧。
4、在我们的Service将Error抛出给Controller层的时候,Controller就必须得自己处理了,不能再继续往外抛了。如果往外抛就会抛到Main线程,有可能导致Main线程中断,程序就崩溃了。为了让我们的代码简洁好看。我们在Controller里面加一个可以捕获到Controller throw的Error。然后做处理,最后返回给前端处理的结果。
@GetMapping(path = TOKEN_ENDPOINT + "/{role}") public ResponseEntity<?> get(@PathVariable("role") String role, @RequestHeader(required = false,value = HttpHeaders.AUTHORIZATION) String visitorToken) throws RestException { String token = tokenService.getToken(FAKE_TOKEN,role); return new ResponseEntity(token,OK); }
@ExceptionHandler(RestException.class) public ResponseEntity<String> throwRestException(RestException restException){ return new ResponseEntity<String>(restException.getMessage(),BAD_REQUEST); }5、最后,再次表明自己的想法,写blog就是为了让其他人在面对相同的问题的时候可以很快的找到相应的解决方案,如果你看到我的博客还是没有解决我的问题,请你给我留言或者评论。我看到了一定会给予你最正向的回复和帮助。感谢。