spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
75k stars 40.66k forks source link

ResponseStatusException threw on method with @ExceptionHandler annotation always return status 500 #26715

Open manoellribeiro opened 3 years ago

manoellribeiro commented 3 years ago

Spring Boot Version: 2.5.0

I'm trying to throw an instance of ResponseStatusException with a status code on a method annotated with @ExceptionHandler, but it is always returning status 500.

What I'm trying to do: Obs: Failure is a custom exception of my project with an error message and error status code.

@RestController
class LoginController(
    @Autowired private val loginUseCase: ActionUseCase<LoginRequest, LoginResponse>
) {

    @PostMapping("/login")
    fun login(
        @RequestBody loginRequest: LoginRequest
    ): LoginResponse {
        return loginUseCase.execute(
            loginRequest
        )
    }

    @ExceptionHandler(Failure::class)
    fun handleException(failure: Failure) {
        throw ResponseStatusException(failure.errorStatus, failure.message)
    }
}

When the method execute throws a Failure, the response I was expecting is:

{
  "timestamp": "2021-05-31T00:32:29.211+00:00",
  "status": 400,
  "error": "Bad Request",
  "message": "reason",
  "path": "path"
}

But I'm always getting:

{
  "timestamp": "2021-05-31T00:32:29.211+00:00",
  "status": 500,
  "error": "Internal Server Error",
  "message": "reason",
  "path": "path"
}

I know that I can use @ResponseStatus annotation on my custom exception and I'm already using that, but I want to report this because I think it's not an expected behavior.

bclozel commented 3 years ago

Currently, Spring Framework and Spring Boot support error handling at several levels.

Spring Framework handles them with ExceptionResolver implementations. They're using annotations on exceptions themselves, or @ExceptionHandler methods at the controller or controller advice levels. The ResponseStatusException can is processed at this level by Spring Framework. This means that a regular controller method throwing such an exception will set the status on the response.

If any exception is not handled at that first level, it's then processed by Spring Boot error handling as part of an error dispatch. Spring Boot support will gather information from the exception and create an error response in HTML or JSON format.

There are two problems here:

It seems that the line is not really clear between fully handling the exception (status, body, etc) and customizing the error before delegating to the Spring Boot error format. I think this is a key problem that we've faced several times in the 1.x and 2.x timeline for Spring Boot.

I'm marking this issue for team discussion, but I think we would need to repurpose this issue and reconsider a broader refactoring in the 3.x timeline.