Skip to content

@ResponseBody does not work with @ExceptionHandler when reason is set in @ResponseStatus [SPR-9159] #13797

@spring-projects-issues

Description

@spring-projects-issues

Tim Reidel opened SPR-9159 and commented

An IOException is thrown by Jetty when returning an object from an exception handler in my @Controller annotated class. Some investigation revealed that the issue was seen only when I defined an error response in the @ResponseStatus annotation.

An example @ExceptionHandler that demonstrates the issue:

@ExceptionHandler(TestException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "test error")
public @ResponseBody
TestObject handleTestException(TestException e)
{
	return new TestObject(e.getMessage());
}

It seems the IOException is triggered by the HTTPConnection being written to twice in ServletInvocableHandlerMethod.invokeAndHandle()

  1. in the call to setResponseStatus() in ServletInvocableHandlerMethod.invokeAndHandle()
  2. in the call to returnValueHandlers.handleReturnValue()

If there is a reason defined in the @ResponseStatus then setResponseStatus() will write output text to the HTTP response which will cause the outputStream to close. Later within returnValueHandlers.handleReturnValue() the JAXB seralization attempts to write to the outputStream which is closed. This triggers the IOException.

Below is a snippet from ServletInvocableHandlerMethod that shows the logic that generates the error page via sendError(). This is only called when this.responseReason is non-null. Thus if you define a reason in the @ResponseStatus annotation the @ResponseBody annotation won't work.

private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
               if (this.responseStatus != null) {
                       if (StringUtils.hasText(this.responseReason)) {
                               webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason);
                       }
                       else {
                               webRequest.getResponse().setStatus(this.responseStatus.value());
                       }
                       // to be picked up by the RedirectView
                       webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus);
               }
       }

Affects: 3.1.1

Referenced from: commits 2295372, d52fc3b

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions