【问题标题】:Spring Boot ExceptionHandler Throwing More ErrorsSpring Boot ExceptionHandler 抛出更多错误
【发布时间】:2017-06-09 19:14:23
【问题描述】:

我正在使用通过注释配置的 Spring Boot,但遇到了异常处理程序会抛出我无法捕获的错误的问题。我想出了如何阻止错误被抛出,但我不知道它为什么会起作用。

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

使用 curl curl -v -H "Accept: application/xhtml xml" 'http://localhost:8080/testEndpoint' 触发此异常会导致记录以下内容:

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...

对客户端的响应是乱七八糟的 HTML 和堆栈跟踪:

<!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...

在尝试记录不同的值时,我修改了我的异常处理程序以将响应作为参数。

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

现在,相同的 curl 只会产生我最初期望的日志,并且不会在下游触发进一步的错误。

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" - 

并且客户端得到的响应是正确的。

我的问题是为什么?在将响应对象作为参数传递给处理程序之前,是否会对上游做一些事情?如果没有发生任何事情,那就是为什么下游会发生后续错误?谢谢。

【问题讨论】:

    标签: spring-boot exception-handling spring-web


    【解决方案1】:

    从您的日志消息中查看此内容:“application/xhtml xml”。正确的格式可能是“application/xhtml+xml”。 SpringMVC 会在控制器方法返回后检查 org.springframework.util.MimeTypeUtils.checkToken(String type) 中的 'Accept' 头,不接受内容类型中的空格,会抛出异常。

    【讨论】:

    • 您知道任何将其设置为空间的客户端/浏览器吗?
    【解决方案2】:

    当 spring 尝试返回错误消息时,它使用请求 'Accept' 标头的媒体类型来找出如何格式化响应。 Spring 解析失败的 'Accept' 标头,因为该值的格式无效并且引发了另一个异常,这就是您所看到的。

    解决方案 您可以使用控制器类或方法上的 @Request 映射来限制 API 接受的媒体类型,例如:

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

    如果您这样做,则根本不接受具有与指定不同的媒体类型的请求,并且将返回 Http 406 'Not Acceptable'。因为没有创建内容,所以描述的问题应该不会再发生了。

    【讨论】:

    • 谢谢,有道理;但我希望无论异常处理程序的方法签名如何,行为都会保持一致。这就是让我在这里绊倒的原因。即使我没有修改响应对象,只要将它作为参数包含在内,Spring 的内部就会自动处理错误。
    猜你喜欢
    • 2017-12-04
    • 2020-06-02
    • 2019-05-16
    • 2021-10-13
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    • 2018-12-01
    • 2018-06-27
    相关资源
    最近更新 更多