Search code examples
javaspringspring-bootexceptionresttemplate

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.


Solution

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