Spring web series RestTemplate 4xx/5xx exception information capture

Keywords: Programming Spring github

200104 spring web series tutorialresttemplate 4xx / 5xx exception information capture

Recently, when using RestTemplate to access external resources, an interesting problem was found. Because the permission verification fails, the http code of 401 returned by the other party will also contain some exception prompt information in the returned data. However, when using RestTemplate to access, the following exception of 401 prompt is thrown directly, and the prompt information cannot be obtained

So what can RestTemplate do if you want to get non-200 status code return data?

<!-- more -->

1. Exception capture

1. Problem analysis

The exception handling of RestTemplate is done with the help of org.springframework.web.client.ResponseErrorHandler. First, look at the two core methods

  • The following code is from spring-web.5.0.7.RELEASE
public interface ResponseErrorHandler {
  // Judge whether there is abnormality
	boolean hasError(ClientHttpResponse response) throws IOException;
  // If there is a problem, enter this method and deal with it
	void handleError(ClientHttpResponse response) throws IOException;
}

In short, when the RestTemplate sends out a request and obtains the corresponding response of the other party, it will give it to the ResponseErrorHandler to determine whether the returned result is ok or not

So next, target the default exception handler of RestTemplate: org.springframework.web.client.DefaultResponseErrorHandler

a. Determine whether the returned result is ok

From the source, it is mainly based on the returned http code to determine whether it is ok

// Judge whether there is a problem according to the returned http code
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
	HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
	return (statusCode != null && hasError(statusCode));
}

// The specific decision logic is simply that the returned http code is standard 4xx and 5xx, so it is considered that there is a problem
protected boolean hasError(HttpStatus statusCode) {
	return (statusCode.series() == HttpStatus.Series.CLIENT_ERROR ||
			statusCode.series() == HttpStatus.Series.SERVER_ERROR);
}

Please note that in the above implementation, some custom HTTP codes will not be considered as exceptions because they cannot be converted to corresponding HttpStatus (described in the following example)

b. Exception handling

When the above hasError returns ture, it will enter the exception handling logic

@Override
public void handleError(ClientHttpResponse response) throws IOException {
	HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
	if (statusCode == null) {
		throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(),
				response.getHeaders(), getResponseBody(response), getCharset(response));
	}
	handleError(response, statusCode);
}

protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
	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 UnknownHttpStatusCodeException(statusCode.value(), response.getStatusText(),
					response.getHeaders(), getResponseBody(response), getCharset(response));
	}
}

It can also be seen from the above that the exception handling logic is very simple, and the exception is thrown directly

2. Exception capture

After locating the problem of raw surface, it's relatively easy to solve the problem again. You can customize an exception handling class and think it's normal no matter what the status code returns

RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
    @Override
    protected boolean hasError(HttpStatus statusCode) {
        return super.hasError(statusCode);
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
    }
});

3. measurement

First, write two results. The returned http status code is not 200. There are many ways to write case s that return non 200 status code. Here are two common ways to write

@RestController
public class HelloRest {
  @GetMapping("401")
  public ResponseEntity<String> _401(HttpServletResponse response) {
      ResponseEntity<String> ans =
              new ResponseEntity<>("{\"code\": 401, \"msg\": \"some error!\"}", HttpStatus.UNAUTHORIZED);
      return ans;
  }

  @GetMapping("525")
  public String _525(HttpServletResponse response) {
      response.setStatus(525);
      return "{\"code\": 525, \"msg\": \"Custom error code!\"}";
  }
}

First, let's take a look at the custom 525 and standard 401 http code, which are directly accessed through the RestTemplate

@Test
public void testCode() {
    RestTemplate restTemplate = new RestTemplate();
    HttpEntity<String> ans = restTemplate.getForEntity("http://127.0.0.1:8080/525", String.class);
    System.out.println(ans);

    ans = restTemplate.getForEntity("http://127.0.0.1:8080/401", String.class);
    System.out.println(ans);
}

It can also be seen from the above output results that non-standard http code does not throw exceptions (the reasons are analyzed above). Next, let's look at the case s where even standard http code does not want to throw exceptions

@Test
public void testSend() {
    String url = "http://127.0.0.1:8080/401";
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
        @Override
        protected boolean hasError(HttpStatus statusCode) {
            return super.hasError(statusCode);
        }

        @Override
        public void handleError(ClientHttpResponse response) throws IOException {
        }
    });
    HttpEntity<String> ans = restTemplate.getForEntity(url, String.class);
    System.out.println(ans);
}

II. other

0. project

1. A grey Blog

The best letter is not as good as the above. It's just a one-of-a-kind remark. Due to the limited personal ability, there are inevitably omissions and mistakes. If you find a bug or have better suggestions, you are welcome to criticize and correct. Thank you very much

Here is a grey personal blog, recording all the blogs in study and work. Welcome to visit

Posted by jsucupira on Fri, 10 Jan 2020 23:03:45 -0800