Spring MV 3.2 Mapping Exception Responses

Spring 3.2.15, MVC-based REST API (not Spring Download, unfortunately!). I am trying to implement an exception handler / handler that meets the following criteria:

  • No matter what happens (success or failure), the Spring application always returns a response object MyAppResponse(see below); and
  • If the request was processed successfully, return the HTTP status 200 (typical); and
  • In the case of request processing and an exception occurs, I need to control the mapping of a specific exception to a specific HTTP status code
    • Spring MVC environment errors (e.g. BlahException) should map to HTTP 422
    • User application exceptions, such as my FizzBuzzException, have their own state mapping schemes:
      • FizzBuzzException → HTTP 401
      • FooBarException → HTTP 403
      • OmgException → HTTP 404
    • All other exceptions, that is, Spring exceptions and exceptions for non-standard applications (listed above 3), must contain HTTP 500

If the object MyAppResponse:

// Groovy pseudo-code
@Canonical
class MyAppResponse {
    String detail
    String randomNumber
}

It seems that it ResponseEntityExceptionHandlercan do this for me, but I do not see the forest through the wrt trees as it receives the arguments passed. I hope I can do something like:

// Groovy-pseudo code
@ControllerAdvice
class MyAppExceptionMapper extends ResponseEntityExceptionHandler {
    ResponseEntity<Object> handleFizzBuzzException(FizzBuzzException fbEx, HttpHeaders headers, HttpStatus status) {
        // TODO: How to reset status to 401?
        status = ???

        new ResponseEntity(fbEx.message, headers, status)
    }

    ResponseEntity<Object> handleFooBarException(FooBarException fbEx, HttpHeaders headers, HttpStatus status) {
        // TODO: How to reset status to 403?
        status = ???

        new ResponseEntity(fbEx.message, headers, status)
    }

    ResponseEntity<Object> handleOmgException(OmgException omgEx, HttpHeaders headers, HttpStatus status) {
        // TODO: How to reset status to 404?
        status = ???

        new ResponseEntity(omgEx.message, headers, status)
    }

    // Now map all Spring-generated exceptions to 422
    ResponseEntity<Object> handleAllSpringExceptions(SpringException springEx, HttpHeaders headers, HttpStatus status) {
        // TODO: How to reset status to 422?
        status = ???

        new ResponseEntity(springEx.message, headers, status)
    }

    // Everything else is a 500...
    ResponseEntity<Object> handleAllOtherExceptions(Exception ex, HttpHeaders headers, HttpStatus status) {
        // TODO: How to reset status to 500?
        status = ???

        new ResponseEntity("Whoops, something happened. Lol.", headers, status)
    }
}

Any idea how I can fully implement this matching logic and requiring the object to be an instance MyAppResponse, not just a string?

Then, annotates the class with the @ControllerAdviceonly thing I need to do to configure Spring to use it?

+4
source share
2 answers

@bond-java-bond, ResponseEntity:

  • @ResponseStatus handleSomeException (, @ResponseStatus(HttpStatus.UNAUTHORIZED))
  • MyAppResponse

( HTTP-), MyAppExceptionMapper :

@ControllerAdvice
public class MyAppExceptionMapper {
    private final Map<Class<?>, HttpStatus> map;
    {
        map = new HashMap<>();
        map.put(FizzBuzzException.class, HttpStatus.UNAUTHORIZED);
        map.put(FooBarException.class, HttpStatus.FORBIDDEN);
        map.put(NoSuchRequestHandlingMethodException.class, HttpStatus.UNPROCESSABLE_ENTITY);
        /* List Spring specific exceptions here as @bond-java-bond suggested */
        map.put(Exception.class, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    // Handle all exceptions
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseEntity<MyAppResponse> handleException(Exception exception) {
        MyAppResponse response = new MyAppResponse();
        // Fill response with details

        HttpStatus status = map.get(exception.getClass());
        if (status == null) {
            status = map.get(Exception.class);// By default
        }

        return new ResponseEntity<>(response, status);
    }
}

:

  • .
  • .
  • .
  • .

, .

MVC

, , mvc-dispatcher-servlet.xml ( contextConfigLocation web.xml) :

<context:component-scan base-package="base.package"/>
<mvc:annotation-driven/>

-, , @ControllerAdvice @Controller base.package.

. Spring MVC Spring MVC @ExceptionHandler Example .

+1

/ .

, ( REST), @RequestMapping,

@RequestMapping(value = "/demo", method = RequestMethod.GET)
@ResponseStatus(value = HttpStatus.OK)
public MyAppResponse doSomething() { .... }

HTTP () @ControllerAdvice, ( )

@ControllerAdvice
public class CustomExceptionHandler {

    // Handle FizzBuzzException with status code as 401
    @ExceptionHandler(value = FizzBuzzException.class)
    @ResponseBody
    public ResponseEntity<MyAppResponse> handleException(FizzBuzzException ex) {
        return new ResponseEntity<MyAppResponse>(buildResponse(ex), HttpStatus.UNAUTHORIZED);
    }

    // Handle FooBarException with status code as 403
    @ExceptionHandler(value = FooBarException.class)
    @ResponseBody
    public ResponseEntity<MyAppResponse> handleException(FooBarException ex) {
        return new ResponseEntity<MyAppResponse>(buildResponse(ex), HttpStatus.FORBIDDEN);
    }

    // Handle OmgException with status code as 404
    @ExceptionHandler(value = OmgException.class)
    @ResponseBody
    public ResponseEntity<MyAppResponse> handleException(OmgException ex) {
        return new ResponseEntity<MyAppResponse>(buildResponse(ex), HttpStatus.NOT_FOUND);
    }

    // handle Spring MVC specific exceptions with status code 422
    @ExceptionHandler(value = {NoSuchRequestHandlingMethodException.class, HttpRequestMethodNotSupportedException.class, HttpMediaTypeNotSupportedException.class,
        HttpMediaTypeNotAcceptableException.class, MissingPathVariableException.class, MissingServletRequestParameterException.class, ServletRequestBindingException.class,
        ConversionNotSupportedException.class, TypeMismatchException.class, HttpMessageNotReadableException.class, HttpMessageNotWritableException.class, MethodArgumentNotValidException.class,
        MissingServletRequestPartException.class, BindException.class, NoHandlerFoundException.class, AsyncRequestTimeoutException.class})
    @ResponseBody
    public ResponseEntity<MyAppResponse> handleException(Exception ex) {
        return new ResponseEntity<MyAppResponse>(buildResponse(ex), HttpStatus.UNPROCESSABLE_ENTITY);
    }

    // Handle rest of the exception(s) with status code as 500
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseEntity<MyAppResponse> handleException(Exception ex) {
        return new ResponseEntity<MyAppResponse>(buildResponse(ex), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    private MyAppResponse buildResponse(Throwable t) {
        MyAppResponse response = new MyAppResponse();
        // supply value to response object
        return response;
    }
}

, - .

P.S.: Spring MVC

+1

Source: https://habr.com/ru/post/1663998/


All Articles