简体   繁体   中英

Spring Boot Returning 401 Statused Custom Object From Exception Handler

I have an Spring Boot rest API that have an LoginController for doing simple authentication processes. This is the token validation action.

@PostMapping("validatetoken")
public ResponseEntity<ValidateTokenResponse> validateToken(
        @RequestBody ValidateTokenRequest validateTokenRequest, HttpServletRequest request) throws Exception {

    if (StringUtils.isEmpty(validateTokenRequest.getToken())) throw new AuthenticationFailedException("token parameter cannot be null or empty");

    Boolean isValid = authenticationService.validateToken(request, validateTokenRequest.getToken());
    ValidateTokenResponse response = new ValidateTokenResponse();
    response.setIsValid(isValid);
    response.setToken(validateTokenRequest.getToken());
    return new ResponseEntity<>(response, isValid? HttpStatus.OK : HttpStatus.UNAUTHORIZED);
}

And in my api I'm catching all errors in a ResponseEntityExceptionHandler and converting a custom object this way.

@ExceptionHandler(AuthenticationFailedException.class)
@ResponseBody
protected ResponseEntity<Object> handleAuthenticationFailed(AuthenticationFailedException ex) {
    LogManager.error(ex);
    ApiError apiError = new ApiError(HttpStatus.UNAUTHORIZED);
    apiError.setMessage(ex.getMessage());
    return buildResponseEntity(apiError);
}

But when I want to call this api using RestTemplate like below I'm getting and exception like java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode .

ResponseEntity<String> responseEntity =
    restTemplate.exchange(
        this.validateTokenUrl,
        HttpMethod.POST,
        requestHttpEntity,
        String.class);

But if I change HttpStatus from ExceptionHandler to except HttpStatus.UNAUTHORIZED I can get true ApiError object from the client. What can cause this problem and how can I resolve it?

EDIT: Created a github repo that mimics my problemic parts of my project.

By default RestTemplate uses DefaultResponseErrorHandler . This error handler throws exception whenever 4xx/5xx response is send from REST API.

If you want custom error handling, just register your custom error handler via restTemplate.setErrorHandler , where you would use your implementation of ResponseErrorHandler interface.

To make it clear, I suggest to autowire resttemplate in separate config file with below detail

@Bean
  public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setErrorHandler(new ErrorHandler());

    SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
    requestFactory.setOutputStreaming(false);

    restTemplate.setRequestFactory(requestFactory);

    return restTemplate;
  }

This is a default behavor for SimpleClientHttpRequestFactory (uses the JDK's internal http client)

A simple fix is to use apache http components library:

ClientHttpRequestFactory  requestFactory = new HttpComponentsClientHttpRequestFactory();
restTemplate.setRequestFactory(requestFactory);

see this link for more details: https://github.com/spring-projects/spring-security-oauth/issues/441#issuecomment-92033542

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM