Skip to content

Make DefaultErrorAttributes easier to subclass for message customization #22378

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

Closed
ldwqh0 opened this issue Jul 17, 2020 · 2 comments
Closed
Assignees
Labels
type: enhancement A general enhancement
Milestone

Comments

@ldwqh0
Copy link

ldwqh0 commented Jul 17, 2020

I have an exception like this

@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class RequestValidationException extends RuntimeException {

    private static final long serialVersionUID = -3157465183026609933L;

    public RequestValidationException() {
        super();
    }

    public RequestValidationException(String message) {
        super(message);
    }
}

And then ,I throw the exception in a controller

throw new RequestValidationException("some message");

if I use tomcat containner ,the response is like this

{
    "timestamp": "2020-07-17T09:22:41.845+0000",
    "status": 400,
    "error": "Bad Request",
    "message": "some message",
    "path": "/oauth/token"
}

but if I use undertow or jetty containner ,the response is like this

{
    "timestamp": "2020-07-17T09:22:41.845+0000",
    "status": 400,
    "error": "Bad Request",
    "message": "Bad Request",
    "path": "/oauth/token"
}

there is different with message field.

I think it a Bug.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jul 17, 2020
@wilkinsona
Copy link
Member

wilkinsona commented Jul 17, 2020

Thanks for the report. This an interesting difference in servlet container behaviour.

Spring Framework's ResponseStatusExceptionResolver calls response.sendError(statusCode). When using Tomcat, this results in the javax.servlet.error.message request attribute being set to an empty string. When using Jetty or Undertow, they provide a default message based on the status code. That message is "Bad Request" in this case. When determining the message to include in the error response, Spring Boot's DefaultErrorAttributes uses the value of the javax.servlet.error.message request attribute if it is a non-empty string. If it's null or empty, it uses the message from the exception, if any, that was thrown. If that's null or empty it defaults to "No message available".

Unfortunately, there's no way for Spring Boot to know if it should prefer the exception's error message or the value of the javax.servlet.error.message request attribute as the error handling code doesn't know exactly how or why they have been set. All we can do is pick one and use it consistently as we have done.

You can customise the behaviour described above by providing your own ErrorAttributes bean, however it isn't as it should be so Im going to leave this issue open for now to see what, if anything, we could do to improve it. Here's one way to prefer the exception message over the javax.servlet.error.message request attribute at the moment:

@Bean
public ErrorAttributes customErrorAttributes() {
    return new DefaultErrorAttributes() {

        @Override
        public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
            Map<String, Object> attributes = super.getErrorAttributes(webRequest, options);
            Throwable error = getError(webRequest);
            if (error == null) {
                return attributes;
            }
            String message = error.getMessage();
            if (StringUtils.isEmpty(message)) {
                message = getAttribute(webRequest, RequestDispatcher.ERROR_MESSAGE);
            }
            if (StringUtils.isEmpty(message)) {
                message = "No message available";
            }
            attributes.put("message", message);
            return attributes;
        }
        
        @SuppressWarnings("unchecked")
        private <T> T getAttribute(RequestAttributes requestAttributes, String name) {
            return (T) requestAttributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST);
        }
        
    };
}

@wilkinsona wilkinsona added the for: team-attention An issue we'd like other members of the team to review label Jul 17, 2020
@ldwqh0
Copy link
Author

ldwqh0 commented Jul 17, 2020

@wilkinsona
Yes.
If I want to display more information, like reason in @ResponseStatus. I can customize my ErrorAttributes Bean.
I mean different containers need to maintain consistent behavior.
By default, I prefer to display error messages.

@philwebb philwebb changed the title There is a different Error handler with tomcat and undertow containner! There is a different Error handler with tomcat and undertow container! Jul 17, 2020
@philwebb philwebb added this to the 2.4.x milestone Jul 17, 2020
@philwebb philwebb added type: enhancement A general enhancement and removed for: team-attention An issue we'd like other members of the team to review status: waiting-for-triage An issue we've not yet triaged labels Jul 17, 2020
@philwebb philwebb changed the title There is a different Error handler with tomcat and undertow container! Make DefaultErrorAttributes easier to subclass for customization Jul 17, 2020
@philwebb philwebb changed the title Make DefaultErrorAttributes easier to subclass for customization Make DefaultErrorAttributes easier to subclass for message customization Jul 17, 2020
@wilkinsona wilkinsona self-assigned this Jul 21, 2020
@wilkinsona wilkinsona modified the milestones: 2.4.x, 2.4.0-M2 Jul 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants