spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.18k stars 40.68k forks source link

@RestControllerAdvice with @PostMapping consumes #14984

Closed NasirSepehri closed 6 years ago

NasirSepehri commented 6 years ago

I Use spring boot 2.0.6 I have a mapping like below path:

@RestController
@RequestMapping(value = {"/"})
public class A {

    @PostMapping(value = {"test"}, consumes = MediaType.APPLICATION_JSON_VALUE)
    public String getResponse(@RequestBody String message) throws Exception{
        return "Hello world!";
    }
}

It's work when I use below snippet code in the same class( class A)

@RestControllerAdvice
@Order(value = Ordered.HIGHEST_PRECEDENCE)
@RestController
@RequestMapping(value = {"/"})
public class A {

    @ExceptionHandler({HttpMediaTypeNotSupportedException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ResponseEntity<String> processException(HttpMediaTypeNotSupportedException  exception){
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body("error");
    }

    @PostMapping(value = {"test"}, consumes = MediaType.APPLICATION_JSON_VALUE)
    public String getResponse(@RequestBody String message) throws Exception{
        return "Hello world!";
    }
}

but when I use separate class with @RestControllerAdvice(assignableTypes = A.class) annotation, it does not work.

@RestControllerAdvice(assignableTypes = A.class)
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class globalExceptionHandler{

    @ExceptionHandler({HttpMediaTypeNotSupportedException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ResponseEntity<String> processException(HttpMediaTypeNotSupportedException exception){
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body("error");
    }
}

Is this a bug or my code is incorrect?

philwebb commented 6 years ago

I don't think this is a bug. The reason your GlobalExceptionHandler isn't triggered is because of assignableTypes = A.class. Spring MVC hasn't found any suitable mappings so there is no handler available when the HttpMediaTypeNotSupportedException is thrown. If you drop the assignableTypes attribute things work as expected.

NasirSepehri commented 6 years ago

Hi @philwebb , I write a sample code and upload it here. This code work correctly for MethodArgumentNotValidException exception but does not work for HttpMediaTypeNotSupportedException exception. I use http://localhost:8080/a and http://localhost:8080/b URL's. json body is { "name":"Nasir", "age":32 }

wilkinsona commented 6 years ago

@Nasir-S That’s exactly the same problem that Phil has explained above and I have tried to explain on Stack Overflow.

It works for MethodArgumentNotValidException because a specific handler method has been identified as the handler of the request by the time the exception is thrown. This means that the assignable type on your controller advice matches against the handler’s class so the advice is used.

It does not work for HttpMediaTypeNotSupportedException because there is no handler method for the request. There is no handler method because none of the potential handlers can consume the request’s content type. As a result the assignable type on your controller advice prevents it from being able to handle the exception.

Putting this another way, it is impossible to handle this HttpMediaTypeNotSupportedException in controller advice restricted by assignable type. This has nothing to do with Spring Boot itself but is due to the way that Spring MVC works.

NasirSepehri commented 6 years ago

ThanQ @wilkinsona.