Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@ControllerAdvice doesn't handle exceptions

I am using Spring Boot 1.5.9 for developing my application. I need implement jwt authentication, and I used jjwt library. The following code is from my custom authentication security filter which inherits from OncePerRequestFilter. Here I tried to parse the username from token, when username is parsing automatically is jwt verified and also check expiration of token. I debug it and it works, so I next want to send the correct message to the client app why authentication failed. I want to throw an ExpiredJwtException and handle it with the controller advice where I format the output.

Here is exception throwing:

try {
    username = jwtTokenService.getUsername(authToken);
} catch (IllegalArgumentException e) {
    logger.error("an error occured during getting username from token", e);
} catch (ExpiredJwtException e) {
    logger.warn("the token is expired and not valid anymore", e);
    throw new ExpiredJwtException(e.getHeader(), e.getClaims(), e.getMessage());
}

And here is my controller Advice, JwtException is base class of ExpiredJwtException which I throw so it should work. I also tried directly use ExpiredJwtException in ExceptionHandler, but didn't work as well. Next I want to handle another exceptions with same way.

@ControllerAdvice
public class GlobalControllerExceptionHandler {

    @ExceptionHandler(Exception.class)
    public @ResponseBody
    ResponseEntity<Map<String, Object>> handleException(Exception ex) {
        Map<String, Object> errorInfo = new HashMap<>();
        errorInfo.put("message", ex.getMessage());
        errorInfo.put("status", HttpStatus.BAD_REQUEST);
        errorInfo.put("status_code", HttpStatus.BAD_REQUEST.value());
        return new ResponseEntity<>(errorInfo, HttpStatus.BAD_REQUEST);
    }


    @ExceptionHandler(JwtException.class)
    //@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
    public @ResponseBody
    ResponseEntity handleJwtException(JwtException ex) {
        Map<String, Object> errorInfo = new HashMap<>();
        errorInfo.put("message", ex.getLocalizedMessage());
        errorInfo.put("status", HttpStatus.UNPROCESSABLE_ENTITY);
        errorInfo.put("status_code", HttpStatus.UNPROCESSABLE_ENTITY.value());
        return new ResponseEntity<>(errorInfo, HttpStatus.UNPROCESSABLE_ENTITY);
    }

}

Here is my folder structure: enter image description here

I want return just response with 4xx status, but I always got 5xx Internal error when my exception is thrown. Can you tell me what is wrong with my code? Thanks in advice.

like image 602
Denis Stephanov Avatar asked Sep 15 '26 18:09

Denis Stephanov


2 Answers

If the exception is thrown in filter, Springs exception handling (@ControllerAdvice, @ExceptionHandler) is not involved. You need to catch all exceptions inside filter and work directly with ServletResponse.

As I understand - Filters are low level logic (request handling before spring infrastructure), but you can have a workaround, like a specific filter that wraps chaining and catches all RuntimeExceptions. (Looks like a crunch, but no other solutions).

If you want to have a specific login to create your exception object - override ErrorAttributes bean. It will allow you to have a single view for all application exceptions.

To directly specify http response status use
httpServletResponse.setStatus(... your status code ...);

like image 172
Bogdan Oros Avatar answered Sep 18 '26 08:09

Bogdan Oros


Have your controller extend ResponseEntityExceptionHandler and have your exception handling methods take in the WebRequest as a parameter

Then change your return value to this

return handleExceptionInternal(ex, errorInfo, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);

The HttpStatus.BAD_REQUEST can be changed to any 40x error

Example for Exception.class

@ExceptionHandler(value = { Exception.class })
protected ResponseEntity<Object> handleUncaughtException(Exception ex, WebRequest request) {

  String message = "Something bad happened";

  return handleExceptionInternal(ex, message, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}

According to this Make simple servlet filter work with @ControllerAdvice you can create a custom handler.

Then add your new handler to your WebSecurityConfigurerAdapter

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new CustomHandler());
}
like image 45
locus2k Avatar answered Sep 18 '26 09:09

locus2k



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!