首页 > 解决方案 > 显示使用 Jetty 9.4 发送到 JSP 的 PUT 请求的自定义错误页面

问题描述

背景

从 Spring 4 升级到 5.3 时,指定的错误页面web.xml不再适用于某些 HTTP 动词,其中之一是PUT. 例如,当PUT对 Spring 控制器的请求导致意外错误时,web.xml不会显示中定义的错误页面。相反,我们得到了一个错误405 Method Not Allowed。错误页面已正确显示GET请求。错误页面被(并且仍然)定义如下web.xml

<error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/uncaughtException</location>
</error-page>

“uncaughtException”是一个这样定义的图块视图:

<definition extends="siteDefault" name="uncaughtException">
     <put-attribute name="body" value="/WEB-INF/views/uncaughtException.jspx"/>
</definition>

为了让所有 HTTP 动词都能正常工作,我们添加了这个 Spring Controller:

@RequestMapping("/uncaughtException")
@Controller
public class UncaughtExceptionViewController {

    @RequestMapping
    public String uncaughtException() {
        return "uncaughtException";
    }

}

现在,即使对于PUT请求,错误页面也能正确显示。

以前我们使用 Jetty 9.2,但在升级到 Java 14 时,我们也被迫升级到 Jetty 9.4。执行此操作时,我们注意到错误页面不再显示PUT请求(它适用于GET),即使我们仍然拥有UncaughtExceptionViewController控制器。相反,405 Method Not Allowed错误又回来了。在调试过程中,我们注意到使用 Jetty 9.4 时没有调用uncaughtExceptionin 。UncaughtExceptionViewController

问题

我们如何配置 Jetty 或 Spring MVC 以显示错误页面,即使是PUT请求,而不是只GET显示也不显示405 Method Not Allowed

更新

在设置断点后,org.eclipse.jetty.server.Response.setStatus我发现问题似乎是“JSP 只允许 GET、POST 或 HEAD。Jasper 也允许 OPTIONS”。完整的堆栈跟踪是:

sendError:454, Response (org.eclipse.jetty.server)
sendError:158, HttpServletResponseWrapper (javax.servlet.http)
sendError:119, OnCommittedResponseWrapper (org.springframework.session.web.http)
sendError:158, HttpServletResponseWrapper (javax.servlet.http)
sendError:158, HttpServletResponseWrapper (javax.servlet.http)
sendError:119, OnCommittedResponseWrapper (org.springframework.security.web.util)
sendError:158, HttpServletResponseWrapper (javax.servlet.http)
sendError:119, OnCommittedResponseWrapper (org.springframework.security.web.util)
_jspService:1, site_005fdefault_jspx (org.apache.jsp.WEB_002dINF.layouts)
service:71, HttpJspBase (org.apache.jasper.runtime)
service:790, HttpServlet (javax.servlet.http)
service:476, JspServletWrapper (org.apache.jasper.servlet)
serviceJspFile:386, JspServlet (org.apache.jasper.servlet)
service:330, JspServlet (org.apache.jasper.servlet)
service:106, JettyJspServlet (org.eclipse.jetty.jsp)
service:790, HttpServlet (javax.servlet.http)
service:1402, ServletHolder$NotAsyncServlet (org.eclipse.jetty.servlet)
handle:763, ServletHolder (org.eclipse.jetty.servlet)
doHandle:569, ServletHandler (org.eclipse.jetty.servlet)
handle:143, ScopedHandler (org.eclipse.jetty.server.handler)
handle:620, SecurityHandler (org.eclipse.jetty.security)
handle:127, HandlerWrapper (org.eclipse.jetty.server.handler)
nextHandle:235, ScopedHandler (org.eclipse.jetty.server.handler)
doHandle:1610, SessionHandler (org.eclipse.jetty.server.session)
nextHandle:233, ScopedHandler (org.eclipse.jetty.server.handler)
doHandle:1377, ContextHandler (org.eclipse.jetty.server.handler)
nextScope:188, ScopedHandler (org.eclipse.jetty.server.handler)
doScope:507, ServletHandler (org.eclipse.jetty.servlet)
doScope:1580, SessionHandler (org.eclipse.jetty.server.session)
nextScope:186, ScopedHandler (org.eclipse.jetty.server.handler)
doScope:1292, ContextHandler (org.eclipse.jetty.server.handler)
handle:141, ScopedHandler (org.eclipse.jetty.server.handler)
forward:219, Dispatcher (org.eclipse.jetty.server)
forward:78, Dispatcher (org.eclipse.jetty.server)
forward:407, SessionRepositoryFilter$SessionRepositoryRequestWrapper$SessionCommittingRequestDispatcher (org.springframework.session.web.http)
forward:265, ServletRequest (org.apache.tiles.request.servlet)
doForward:228, ServletRequest (org.apache.tiles.request.servlet)
dispatch:57, AbstractClientRequest (org.apache.tiles.request)
render:47, DispatchRenderer (org.apache.tiles.request.render)
render:259, BasicTilesContainer (org.apache.tiles.impl)
render:397, BasicTilesContainer (org.apache.tiles.impl)
render:238, BasicTilesContainer (org.apache.tiles.impl)
render:221, BasicTilesContainer (org.apache.tiles.impl)
render:59, DefinitionRenderer (org.apache.tiles.renderer)
renderMergedOutputModel:147, TilesView (org.springframework.web.servlet.view.tiles3)
render:316, AbstractView (org.springframework.web.servlet.view)
render:1373, DispatcherServlet (org.springframework.web.servlet)
processDispatchResult:1118, DispatcherServlet (org.springframework.web.servlet)
doDispatch:1057, DispatcherServlet (org.springframework.web.servlet)
doService:943, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doPut:920, FrameworkServlet (org.springframework.web.servlet)
service:710, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:790, HttpServlet (javax.servlet.http)
handle:763, ServletHolder (org.eclipse.jetty.servlet)
doFilter:1651, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilter:226, WebSocketUpgradeFilter (org.eclipse.jetty.websocket.server)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilterInternal:186, OpenEntityManagerInViewFilter (org.springframework.orm.jpa.support)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilter:317, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
invoke:127, FilterSecurityInterceptor (org.springframework.security.web.access.intercept)
doFilter:91, FilterSecurityInterceptor (org.springframework.security.web.access.intercept)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:114, ExceptionTranslationFilter (org.springframework.security.web.access)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:137, SessionManagementFilter (org.springframework.security.web.session)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:111, AnonymousAuthenticationFilter (org.springframework.security.web.authentication)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:158, RememberMeAuthenticationFilter (org.springframework.security.web.authentication.rememberme)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:170, SecurityContextHolderAwareRequestFilter (org.springframework.security.web.servletapi)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:63, RequestCacheAwareFilter (org.springframework.security.web.savedrequest)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:158, BasicAuthenticationFilter (org.springframework.security.web.authentication.www)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:200, AbstractAuthenticationProcessingFilter (org.springframework.security.web.authentication)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:116, LogoutFilter (org.springframework.security.web.authentication.logout)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:66, HeaderWriterFilter (org.springframework.security.web.header)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:56, WebAsyncManagerIntegrationFilter (org.springframework.security.web.context.request.async)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:105, SecurityContextPersistenceFilter (org.springframework.security.web.context)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:214, FilterChainProxy (org.springframework.security.web)
doFilter:177, FilterChainProxy (org.springframework.security.web)
invokeDelegate:358, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:271, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilterInternal:94, HiddenHttpMethodFilter (org.springframework.web.filter)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilterInternal:40, CorsFilter (com.mycompany.spring)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilterInternal:141, SessionRepositoryFilter (org.springframework.session.web.http)
doFilter:82, OncePerRequestFilter (org.springframework.session.web.http)
invokeDelegate:358, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:271, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doHandle:567, ServletHandler (org.eclipse.jetty.servlet)
handle:143, ScopedHandler (org.eclipse.jetty.server.handler)
handle:578, SecurityHandler (org.eclipse.jetty.security)
handle:127, HandlerWrapper (org.eclipse.jetty.server.handler)
nextHandle:235, ScopedHandler (org.eclipse.jetty.server.handler)
doHandle:1610, SessionHandler (org.eclipse.jetty.server.session)
nextHandle:233, ScopedHandler (org.eclipse.jetty.server.handler)
doHandle:1377, ContextHandler (org.eclipse.jetty.server.handler)
nextScope:188, ScopedHandler (org.eclipse.jetty.server.handler)
doScope:507, ServletHandler (org.eclipse.jetty.servlet)
doScope:1580, SessionHandler (org.eclipse.jetty.server.session)
nextScope:186, ScopedHandler (org.eclipse.jetty.server.handler)
doScope:1292, ContextHandler (org.eclipse.jetty.server.handler)
handle:141, ScopedHandler (org.eclipse.jetty.server.handler)
handle:191, ContextHandlerCollection (org.eclipse.jetty.server.handler)
handle:146, HandlerCollection (org.eclipse.jetty.server.handler)
handle:127, HandlerWrapper (org.eclipse.jetty.server.handler)
handle:501, Server (org.eclipse.jetty.server)
lambda$handle$1:383, HttpChannel (org.eclipse.jetty.server)
dispatch:-1, 435181768 (org.eclipse.jetty.server.HttpChannel$$Lambda$1064)
dispatch:556, HttpChannel (org.eclipse.jetty.server)
handle:375, HttpChannel (org.eclipse.jetty.server)
onFillable:273, HttpConnection (org.eclipse.jetty.server)
succeeded:311, AbstractConnection$ReadCallback (org.eclipse.jetty.io)
fillable:105, FillInterest (org.eclipse.jetty.io)
run:104, ChannelEndPoint$1 (org.eclipse.jetty.io)
runTask:336, EatWhatYouKill (org.eclipse.jetty.util.thread.strategy)
doProduce:313, EatWhatYouKill (org.eclipse.jetty.util.thread.strategy)
tryProduce:171, EatWhatYouKill (org.eclipse.jetty.util.thread.strategy)
produce:135, EatWhatYouKill (org.eclipse.jetty.util.thread.strategy)
run:-1, 976266910 (org.eclipse.jetty.io.ManagedSelector$$Lambda$1056)
runJob:806, QueuedThreadPool (org.eclipse.jetty.util.thread)
run:938, QueuedThreadPool$Runner (org.eclipse.jetty.util.thread)
run:832, Thread (java.lang)

标签: javajspjettyembedded-jetty

解决方案


由于这是由 JSP 产生的。

可以使用标准的 Servlet 错误页面处理。

WEB-INF/web.xml可以定义为响应状态码 405 。

  <error-page>
    <error-code>405</error-code>
    <location>/myMethodNotAllowedPath</location>
  </error-page>

您还可以选择定义一个全局错误页面错误处理程序,如下所示..

  <error-page>
    <location>/myGlobalErrorHandler</location>
  </error-page>

从 Servlet 3.0 开始,<error-page>可以根据状态码<error-code>、异常<exception-type>或什么都没有定义反应(这意味着所有错误都没有被更具体的定义捕获)

Servlet 实现会将请求重新调度到定义<location>的 with DispatcherType.ERROR,原始请求的详细信息可以在常量HttpServletRequest.getAttribute(String)中定义的各种键/名称下的值中找到。RequestDispatcher.ERROR_*


推荐阅读