ArmedChef
ArmedChef

Reputation: 106

Spring Boot ExceptionHandler Throwing More Errors

I'm using Spring Boot configured via annotations and I ran into an issue where the exception handler would throw errors that I couldn't catch. I figured out how to stop the error from being thrown but I have no idea why it works.

@ControllerAdvice
public class MyExceptionAdvice {
    ...snip...
    @ResponseBody
    @ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
    void mediaTypeExceptionHandler(HttpMediaTypeNotAcceptableException e) {
        logger.info("exception: {}", e.getMessage());
    }
}

Triggering this exception with a curl curl -v -H "Accept: application/xhtml xml" 'http://localhost:8080/testEndpoint' resulted in the following being logged:

2017-01-24 11:08:20 [http-nio-8080-exec-1] [INFO] MyExceptionAdvice - exception: Could not parse 'Accept' header [application/xhtml xml]: Invalid mime type "application/xhtml xml": Invalid token character ' ' in token "xhtml xml" - 

2017-01-24 11:08:20 [http-nio-8080-exec-1] [WARN] org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - Failed to invoke @ExceptionHandler method: void MyExceptionAdvice.mediaTypeExceptionHandler(javax.servlet.http.HttpServletRequest,org.springframework.web.HttpMediaTypeNotAcceptableException) - org.springframework.web.HttpMediaTypeNotAcceptableException: Could not parse 'Accept' header [application/xhtml xml]: Invalid mime type "application/xhtml xml": Invalid token character ' ' in token "xhtml xml"
    at org.springframework.web.accept.HeaderContentNegotiationStrategy.resolveMediaTypes(HeaderContentNegotiationStrategy.java:59)
    ...snip...

2017-01-24 11:08:20 [http-nio-8080-exec-1] [WARN] org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver - Handling of [org.springframework.web.HttpMediaTypeNotAcceptableException] resulted in Exception - java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
    at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:472)
    ...snip...

2017-01-24 11:08:20 [http-nio-8080-exec-1] [ERROR] org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Could not parse 'Accept' header [application/xhtml xml]: Invalid mime type "application/xhtml xml": Invalid token character ' ' in token "xhtml xml"] with root cause - org.springframework.web.HttpMediaTypeNotAcceptableException: Could not parse 'Accept' header [application/xhtml xml]: Invalid mime type "application/xhtml xml": Invalid token character ' ' in token "xhtml xml"
    at org.springframework.web.accept.HeaderContentNegotiationStrategy.resolveMediaTypes(HeaderContentNegotiationStrategy.java:59)
    ...snip...

And the response to the client was mess of HTML and a stack trace:

<!DOCTYPE html><html><head><title>Apache Tomcat/8.5.6 - Error report</title><style type="text/css">H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}.line {height: 1px; background-color: #525D76; border: none;}</style> </head><body><h1>HTTP Status 500 - Could not parse 'Accept' header [application/xhtml xml]: Invalid mime type &quot;application/xhtml xml&quot;: Invalid token character ' ' in token &quot;xhtml xml&quot;</h1><div class="line"></div><p><b>type</b> Exception report</p><p><b>message</b> <u>Could not parse 'Accept' header [application/xhtml xml]: Invalid mime type &quot;application/xhtml xml&quot;: Invalid token character ' ' in token &quot;xhtml xml&quot;</u></p><p><b>description</b> <u>The server encountered an internal error that prevented it from fulfilling this request.</u></p><p><b>exception</b></p><pre>org.springframework.web.HttpMediaTypeNotAcceptableException: Could not parse 'Accept' header [application/xhtml xml]: Invalid mime type &quot;application/xhtml xml&quot;: Invalid token character ' ' in token &quot;xhtml xml&quot;
org.springframework.web.accept.HeaderContentNegotiationStrategy.resolveMediaTypes(HeaderContentNegotiationStrategy.java:59)
...snip...

While playing around with logging different values I modified my exception handler to have the Response as a parameter.

@ResponseBody
@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
void mediaTypeExceptionHandler(HttpServletResponse response, HttpMediaTypeNotAcceptableException e) {
    logger.info("exception: {}", e.getMessage());
}

Now the same curl only results in the log I was expecting in the first place and no further errors are triggered downstream.

2017-01-24 11:30:31 [http-nio-8080-exec-1] [INFO] com.example.MyExceptionAdvice - exception: Could not parse 'Accept' header [application/xhtml xml]: Invalid mime type "application/xhtml xml": Invalid token character ' ' in token "xhtml xml" - 

And the response the client is getting is correct.

My question is why? Does having the response object as a parameter do something to it upstream before passing it to the handler? If whatever that is doesn't happen is that why the subsequent errors occur downstream? Thanks.

Upvotes: 3

Views: 1920

Answers (2)

Jie.Wu
Jie.Wu

Reputation: 1

Take a look at this from your log message: "application/xhtml xml" . The correct format may be "application/xhtml+xml". SpringMVC will check the 'Accept' header in org.springframework.util.MimeTypeUtils.checkToken(String type) after the controller method returned, which will not accept a space in the content type and will throw an exception.

Upvotes: 0

When spring tries to return the error message, it uses the media type of the requests 'Accept' header to find out how to format the response. Spring parses the 'Accept' header which fails because the value has an invalid format and another exception is thrown and this is what you see.

Solution You can limit the media types that are accepted by your API with the @Request mapping on the controller class or methods for example :

import org.springframework.http.MediaType;
@RequestMapping( MediaType.APPLICATION_JSON_VALUE)

If you do so, a request with a different media type than specified should not be accepted at all and a Http 406 'Not Acceptable' will be returned. Because no content is created, the problem described should not happen any more.

Upvotes: 1

Related Questions