Spring Boot+Jetty升级后自定义Error Handler未生效,求配置方案
问题背景
使用Spring Boot 3.1.10 + Jetty 11.0.20搭建嵌入式Web服务,此前配置的Server级自定义SilentErrorHandler在版本升级后失效。发送无效请求(如curl -H "Content-Type: invalid" http://localhost:8090)时,系统默认使用JettyEmbeddedErrorHandler返回默认错误页面,而非自定义处理器。
核心原因是Jetty的ErrorHandler.getErrorHandler()逻辑:Context级别的Error Handler优先级高于Server级。Spring Boot升级后会自动在Context层面配置JettyEmbeddedErrorHandler,导致Server级的自定义处理器被覆盖。
解决方案
要让自定义Error Handler始终生效,需要同时替换Server级别和所有Context级别的Error Handler。修改WebServerFactoryCustomizer的实现,遍历Jetty Server的Handler树,找到所有ContextHandler并替换其Error Handler:
public class AcmeApiApp implements WebServerFactoryCustomizer<JettyServletWebServerFactory> { // 其他业务代码... @Override public void customize(JettyServletWebServerFactory factory) { SilentErrorHandler customErrorHandler = new SilentErrorHandler(); JettyServerCustomizer serverCustomizer = server -> { // 设置Server级别的Error Handler server.setErrorHandler(customErrorHandler); // 遍历Handler树,替换所有Context级别的Error Handler replaceContextErrorHandlers(server.getHandler(), customErrorHandler); }; factory.addServerCustomizers(serverCustomizer); } private void replaceContextErrorHandlers(Handler handler, SilentErrorHandler customErrorHandler) { if (handler == null) { return; } if (handler instanceof ContextHandler contextHandler) { contextHandler.setErrorHandler(customErrorHandler); } else if (handler instanceof HandlerCollection handlerCollection) { // 处理Handler集合,递归遍历子Handler for (Handler childHandler : handlerCollection.getHandlers()) { replaceContextErrorHandlers(childHandler, customErrorHandler); } } else if (handler instanceof HandlerWrapper handlerWrapper) { // 处理包装类的Handler,递归获取内部Handler replaceContextErrorHandlers(handlerWrapper.getHandler(), customErrorHandler); } } private static final class SilentErrorHandler extends ErrorHandler { // 自定义错误处理逻辑实现... } // 其他业务代码... }
原理说明
Jetty在处理错误时会优先从当前请求对应的ContextHandler中获取Error Handler,只有当Context级处理器不存在时,才会降级使用Server级的处理器。通过上述代码,我们同时替换了Server和所有Context层面的Error Handler,确保无论错误发生在哪个层级,都会触发自定义的SilentErrorHandler。
内容的提问来源于stack exchange,提问作者oleb

