简体   繁体   中英

How to catch HTTP 405 "Method Not Allowed" exception using @ExceptionHandler?

I created a REST application and added a class to handle exceptions:

@RestControllerAdvice(basePackages = "com.foxminded.university.api.controller")
public class ApiGlobalExceptionHandler extends ResponseEntityExceptionHandler {
    private static final String API_UNHANDLED_EXCEPTION = "REST API reached unhandled exception: %s";
    private static final HttpStatus internalServerError = HttpStatus.INTERNAL_SERVER_ERROR;

    @ExceptionHandler(Exception.class)
    public ResponseEntity<Object> handle(Exception e) {
        ExceptionDetail exceptionDetail = new ExceptionDetail(
            String.format(API_UNHANDLED_EXCEPTION, e.getMessage()),
            internalServerError,
            ZonedDateTime.now(ZoneId.systemDefault()));
        return new ResponseEntity<Object>(exceptionDetail, internalServerError);
    }

    @Override
    public ResponseEntity<Object> handleTypeMismatch(
        TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        ExceptionDetail exceptionDetail = new ExceptionDetail(
            String.format(API_UNHANDLED_EXCEPTION, ex.getMessage()),
            internalServerError,
            ZonedDateTime.now(ZoneId.systemDefault()));
        return new ResponseEntity<Object>(exceptionDetail, internalServerError);
    }

    @Override
    public ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException e, HttpHeaders headers,
        HttpStatus status, WebRequest request) {
        ExceptionDetail exceptionDetail = new ExceptionDetail(
            String.format(API_UNHANDLED_EXCEPTION, e.getMessage()),
            internalServerError,
            ZonedDateTime.now(ZoneId.systemDefault()),
            e.getBindingResult().getFieldErrors());
        return new ResponseEntity<Object>(exceptionDetail, internalServerError);
    }
}

When I send the wrong post request via postman http://localhost:8080/api/groups/jkjk instead of http://localhost:8080/api/groups

It throws me an exception that I can't catch initializing when debugging, neither in the ApiGlobalExceptionHandler class nor in the ResponseEntityExceptionHandler class:

{
    "timestamp": 1604171144423,
    "status": 405,
    "error": "Method Not Allowed",
    "message": "",
    "path": "/api/groups/jkjk"
}

All other exceptions I can catch. How do I catch this exception to add custom handling?

You just need to add a new method to with MethodNotAllowedException in its signature.

@ExceptionHandler(value = MethodNotAllowedException.class)
public ResponseEntity<Object> handleMethodNotAllowedExceptionException(MethodNotAllowedException ex) {
    return buildResponseEntity(HttpStatus.METHOD_NOT_ALLOWED, null, null, ex.getMessage(), null);
}

private ResponseEntity<Object> buildResponseEntity(HttpStatus status, HttpHeaders headers, Integer internalCode, String message, List<Object> errors) {
    ResponseBase response = new ResponseBase() //A generic ResponseBase class
            .success(false)
            .message(message)
            .resultCode(internalCode != null ? internalCode : status.value())
            .errors(errors != null
                    ? errors.stream().filter(Objects::nonNull).map(Objects::toString).collect(Collectors.toList())
                    : null);
    
    return new ResponseEntity<>((Object) response, headers, status);
}

You can customize the buildResponseEntity as you please.

UPDATED

I revisited my answer since it didn't meet your requirement. So, it follows like this:

I send post request for a method that accepts GET . This will fire Request method 'POST' not supported as printed below.

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.Looking up handler method for path /v1/user/profile/1 org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported org.springframework.beans.factory.support.DefaultListableBeanFactory.Returning cached instance of singleton bean 'ethGlobalExceptionHandler'

In this case, there is no need to add @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class) .

In fact if you do so, the following error will be thrown (since it is already handled),

java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.HttpRequestMethodNotSupportedException]:....

So, the solution will be:

@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
        HttpHeaders headers, HttpStatus status, WebRequest request) {
    return buildResponseEntity(HttpStatus.METHOD_NOT_ALLOWED, headers, null, ex.getMessage(), Arrays.asList(""));
}

I found the explanation here Custom handling for 405 error with Spring Web MVC

It says The reason your @ExceptionHandler annotated method never catches your exception is because these ExceptionHandler annotated methods are invoked after a successful Spring controller handler mapping is found. However, your exception is raised before that. The reason your @ExceptionHandler annotated method never catches your exception is because these ExceptionHandler annotated methods are invoked after a successful Spring controller handler mapping is found. However, your exception is raised before that.

And the solution is to extends not from ResponseEntityExceptionHandler class, but from DefaultHandlerExceptionResolver and override it handleHttpRequestMethodNotSupported method.

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