Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ResponseStatusException thrown from exception handler methods is no longer respected #32538

Closed
beytularedzheb opened this issue Mar 26, 2024 · 5 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: invalid An issue that we don't feel is valid

Comments

@beytularedzheb
Copy link

beytularedzheb commented Mar 26, 2024

Hi,
After upgrading from spring boot 3.2.3 (spring framework 6.1.4) to 3.2.4 (spring framework 6.1.5) I noticed that the status code for some validated rest endpoints changed to status code 500 - Internal Server Error when validation errors occur.

For example prior to spring boot 3.2.4 with below exception handler I would get status code 400 but in 3.2.4 it returns 500.

I suspect that it could be due to this change: 5f601ce

@RestControllerAdvice
internal class RestControllerExceptionHandler {
    @ExceptionHandler(ConstraintViolationException::class)
    fun exceptionHandler(e: ConstraintViolationException) {
        throw ResponseStatusException(HttpStatus.BAD_REQUEST, e.message, e)
    }
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Mar 26, 2024
@snicoll snicoll added the in: web Issues in web modules (web, webmvc, webflux, websocket) label Mar 27, 2024
@injae-kim
Copy link
Contributor

injae-kim commented Mar 30, 2024

5f601ce handle exceptions from @ExceptionHandler regardless of whether
thrown immediately or via Publisher.

private static Mono<HandlerResult> handleExceptionHandlerFailure(
	ServerWebExchange exchange, Throwable exception, Throwable invocationEx,
	ArrayList<Throwable> exceptions, InvocableHandlerMethod invocable) {

if (disconnectedClientHelper.checkAndLogClientDisconnectedException(invocationEx)) {
	return Mono.empty();
}

// Any other than the original exception (or a cause) is unintended here,
// probably an accident (e.g. failed assertion or the like).
if (!exceptions.contains(invocationEx) && logger.isWarnEnabled()) {
         // ☝️☝️ if exception is 500 and invocationEx from ExceptionHandler is 400, intend to respect 500 here?
	logger.warn(exchange.getLogPrefix() + "Failure in @ExceptionHandler " + invocable, invocationEx);
}

return Mono.error(exception); // ignore invocationEx and return origin exception!
}

Hmm maybe it's intended behavior from this 5f601ce commit?

// Maybe we can fix it like this?
if (invocationEx != null && invocationEx instanceof ResponseStatusException) {
  return Mono.error(invocationEx);
}

return Mono.error(exception);

Maybe we can fix like above code, let's wait maintainer's opinion~!
If my suggestion makes sense, I'm ready to open fix PR :) thank you!

@bclozel
Copy link
Member

bclozel commented Apr 2, 2024

Also see spring-projects/spring-boot#40148 (comment) for another report of this.

@bclozel
Copy link
Member

bclozel commented Apr 5, 2024

Before the change introduced in #32359, the following would happen:

  1. the exception thrown by the controller would be handled first by the DispatcherHandler by calling the exception handler here.
  2. the @ExceptionHandler rethrows another exception
  3. then the RequestMappingHandlerAdapter would handle this rethrown exception

I think this behavior is invalid and unintended for the following reasons:

Still, we can consider rolling back partially this change as this is disrupting applications in a maintenance release. But I do think that keeping this change around for Spring Framework 6.2 is a good choice.

@bclozel bclozel added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Apr 5, 2024
@bclozel bclozel added this to the 6.1.6 milestone Apr 5, 2024
@bclozel
Copy link
Member

bclozel commented Apr 9, 2024

@mbanaszkiewicz-vonage @beytularedzheb Thanks for raising this issue. I discussed this with @rstoyanchev and we confirmed that the original behavior was invalid in the first place and that exception handlers are supposed to fully handle the exception or re-throw the original exception.

I initially marked this as a bug for 6.1.x as I thought the the handling of exceptions in different phases was maybe a mistake, but it turns out this is by design in WebFlux and that we may consolidate things in the future.

In the meantime, can you let us know whether returning a ResponseEntity would work for your application? We would like to know why you chose this setup in the first place. Thanks!

@bclozel bclozel removed this from the 6.1.6 milestone Apr 9, 2024
@bclozel bclozel added status: waiting-for-triage An issue we've not yet triaged or decided on and removed type: bug A general bug labels Apr 9, 2024
@beytularedzheb
Copy link
Author

beytularedzheb commented Apr 10, 2024

Hi @bclozel,
Thank you for checking it.
It was just an easy & quick way of changing the status code and getting standard error response with already populated fields, like path, request id, etc. (DefaultErrorAttributes).
Actually, as you mentioned ResponseEntity, I realized that I can still use ResponseStatusException and achieve the same effect.
Instead of throwing an exception directly in the exception handler method, I can return it in a Mono:

@RestControllerAdvice
internal class RestControllerExceptionHandler {
    @ExceptionHandler(ConstraintViolationException::class)
    fun exceptionHandler(e: ConstraintViolationException): Mono<Throwable> {
          return Mono.error(ResponseStatusException(HttpStatus.BAD_REQUEST, e.message, e))
    }
}

This should also work for the case you shared above: spring-projects/spring-boot#40148 (comment)

I am closing my ticket.
And thank you again!

@jhoeller jhoeller added status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Apr 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

7 participants