Jetty 12实现无效URL转发至错误页:配置及请求头传递问题
问题背景
从Jetty 11迁移到Jetty 12后,基于Spring框架的应用中,原JettyEmbeddedErrorHandler(ErrorPageErrorHandler的扩展类)对无效URL的转发行为失效。Jetty 12中无效URL的异常由服务器级错误处理器处理,无法直接与Servlet请求、调度器交互实现转发。使用ReHandlingErrorHandler实现错误页跳转后,原请求的URI、Accept头等关键信息未传递到错误处理器,导致错误页无法根据Accept头返回对应格式的响应。
解决方案
通过自定义ReHandlingErrorHandler子类,重写核心方法保留原请求的Headers和URI信息,实现与Jetty 11一致的转发行为:
1. 自定义ReHandlingErrorHandler子类
重写getReHandlePathInContext保存原URI到请求属性,并重写reHandle方法复制原请求的Headers到重处理请求中:
private static class SilentErrorHandler extends org.eclipse.jetty.server.handler.ReHandlingErrorHandler { @Override protected String getReHandlePathInContext(Request request, int code, Throwable cause) { // 将原请求URI存入Servlet标准错误属性,供Spring ErrorController获取 request.setAttribute("javax.servlet.error.request_uri", request.getRequestURI()); return "/error"; } @Override protected boolean reHandle(Request request, Response response, int code, Throwable cause) { // 复制原请求的所有HTTP头信息 HttpFields originalHeaders = request.getHttpFields(); // 创建重处理请求,复用原请求的通道、服务器信息,复制Headers Request reHandleRequest = Request.newRequest(request.getServer(), request.getHttpChannel()) .setURI(getReHandlePathInContext(request, code, cause)) .setMethod(request.getMethod()) // 保留原请求方法(如GET/POST) .setHttpFields(originalHeaders); // 调用父类逻辑执行重处理 return super.reHandle(reHandleRequest, response, code, cause); } SilentErrorHandler(Handler handler) { super(handler); setShowStacks(false); setShowMessageInTitle(false); } }
2. 配置Spring JettyServerCustomizer
保持原配置逻辑,将自定义的SilentErrorHandler设置为服务器的错误处理器:
@Bean public JettyServerCustomizer jettyServerCustomizer(RequestLog requestLog) { return server -> { server.setErrorHandler(new SilentErrorHandler(server.getHandler())); server.setRequestLog(requestLog); // 其他服务器配置... }; }
3. 错误页处理器适配
在Spring的ErrorController或错误页逻辑中,可通过以下方式获取原请求信息:
- 原URI:
request.getAttribute("javax.servlet.error.request_uri") - Accept头:
request.getHeader("Accept")
根据Accept头返回对应格式的响应(如HTML/JSON),确保错误页行为符合预期。
原理说明
Jetty 12的ReHandlingErrorHandler默认重处理时会创建新的Request对象,未携带原请求的Headers和URI信息。通过重写reHandle方法,复用原请求的通道并复制Headers,同时将原URI存入标准Servlet错误属性,可让错误处理器完整获取原请求的关键信息,复现Jetty 11中请求转发到错误页的行为。
内容的提问来源于stack exchange,提问作者Maxim Popov

