After upgrade from Spring Boot 3.1.6 to 3.2.0 the @ExceptionHandler
annotated method within a dedicated @ControllerAdvice
(class) is not called anymore.
A minimal example is:
package com.example.demo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.ProblemDetail;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.UUID;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
@SpringBootApplication
public class DemoApplication {
record Content(String key, String value) {
}
@ControllerAdvice
static class ItemControllerAdvice {
private static final Logger logger = LoggerFactory.getLogger(ItemControllerAdvice.class);
public ItemControllerAdvice() {
logger.info("Create instance of {}.", getClass().getCanonicalName());
}
@ExceptionHandler(HttpMessageNotReadableException.class)
public ProblemDetail handleHttpMessageNotReadable(HttpMessageNotReadableException exception) throws URISyntaxException {
var instance = UUID.randomUUID();
logger.error("Id: " + instance, exception);
var problemDetail = ProblemDetail.forStatus(BAD_REQUEST);
problemDetail.setTitle("Could not read provided data or did not validate.");
problemDetail.setInstance(new URI("urn::uuid" + instance));
return problemDetail;
}
}
@Controller
static class ItemController {
private static final Logger logger = LoggerFactory.getLogger(ItemController.class);
@PutMapping("/items/{item}")
public ResponseEntity<Object> update(@PathVariable String item, @RequestBody List<Content> content) {
logger.info("Update {} with {}.", item, content);
return ResponseEntity.noContent().build();
}
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
application.yml
:
spring:
mvc:
problemdetails:
enabled: true
The expected return value for calling the endpoint with an invalid JSON is (Spring Boot 3.1.6):
$ curl -X PUT --location "http://localhost:8080/items/0" -H "Content-Type: application/json" -d '['
{"type":"about:blank","title":"Could not read provided data or did not validate.","status":400,"instance":"urn::uuid2ea314de-39bf-417e-9f52-796506111b41"}
With Spring Boot 3.2.0 the return value does not contain the custom error message:
curl -X PUT --location "http://localhost:8080/items/0" -H "Content-Type: application/json" -d '['
{"type":"about:blank","title":"Bad Request","status":400,"detail":"Failed to read request","instance":"/items/0"}
The difference in code execution start in org.springframework.web.method.support.InvocableHandlerMethod#doInvoke
. The returned bridged method is different:
Spring Boot 3.1.6:
Spring Boot 3.2.0:
Placing the @ExceptionHandler
in the same class as the @Controller
works for both 3.1.6 and 3.2.0.
Comment From: bclozel
I think this is the result of spring-projects/spring-boot#36288 and can be considered as the expected behavior after that change. This was working "by accident" in the past, in the sense that you custom controller advice and the auto-configured ResponseEntityExceptionHandler
were both ordered at lowest precedence. Any difference on bean scanning could have broken this arrangement.
This is working with an @ExceptionHandler
local controller because this takes precedence over the global ones.
Ordering your custom @ControllerAdvice
with anything before "0" works as expected. Typically, adding a @Order(-1)
to this class fixes your sample.
Sorry this caused an issue in your application - maybe you would like to suggest a documentation change in Spring Boot or Spring Framework about this? Feel create to create another issue or submit a PR if you feel this can help others.
Thanks!
Comment From: agebhar1
Thanks a lot @bclozel for you help. Adding @Order(-1)
fixes the issue such that the @ExceptionHandler
annotated method is invoked as before.
I will take a look on the documentation for possible improvements.
Comment From: JFFigueiredo
Hi @agebhar1 , did you find another solution or stick with the @Order(-1)?
I am having the exact same problem.
Thank you.
Comment From: agebhar1
Hi @JFFigueiredo, I stick to @Order(-1)
and it works like before.
Comment From: bclozel
Please create a new issue and share a minimal application that reproduces the problem.