You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 2.7.0内嵌Jetty开启目录列表访问/foo返回404问题

Spring Boot 2.7.0内嵌Jetty配置目录列表访问/foo返回404问题

问题背景

在Spring Boot v2.7.0版本中使用内嵌Jetty,尝试实现WebServerFactoryCustomizer接口开启目录列表功能,初始实现代码如下:

public class MyJettyWebServerCustomizer implements WebServerFactoryCustomizer<JettyServletWebServerFactory> {

    @Override
    public void customize(JettyServletWebServerFactory factory) {
        JettyServerCustomizer jettyServerCustomizer = new JettyServerCustomizer() {
            
            @Override
            public void customize(Server server) {
                Handler[] childHandlersByClass = server.getChildHandlersByClass(WebAppContext.class);
                final WebAppContext webAppContext = (WebAppContext) childHandlersByClass[0];
                ServletHandler handler = webAppContext.getServletHandler();
                
                ServletHolder defServlet = ((WebAppContext)server.getHandlers()[0]).getServletHandler().getServlets()[0];
                defServlet.setInitParameter("dirAllowed","true");
                defServlet.setInitParameter("resourceBase","/tmp/");
                webAppContext.getServletHandler().addServletWithMapping(defServlet, "/foo");
                webAppContext.getServletHandler().getServletMapping("/foo").setDefault(true);        
            }
            
        };  
        factory.addServerCustomizers(jettyServerCustomizer);
    }
}

访问http://localhost:8080/foo时返回404状态码。后续尝试直接新建Default Servlet的方案排查,问题依旧存在,对应代码片段如下:

ServletHolder holder = new ServletHolder();
holder.setName("default");
holder.setClassName("org.eclipse.jetty.servlet.DefaultServlet");
holder.setInitParameter("dirAllowed", "true");
holder.setInitOrder(1);
webAppContext.getServletHandler().addServletWithMapping(holder, "/foo");
webAppContext.getServletHandler().getServletMapping("/foo").setDefault(true);

疑问为该问题是对底层配置逻辑存在理解偏差,还是遗漏了必要配置项。


问题根因

实现存在3个核心错误,属于对Jetty DefaultServlet加载逻辑和映射规则的认知偏差:

  • 复用已初始化的DefaultServlet修改参数无效:Jetty的Servlet初始化参数仅在Servlet首次启动加载时读取,拿到容器内置的DefaultServlet时,它已经完成初始化,后续再调用setInitParameter修改dirAllowed、resourceBase不会产生任何效果。
  • 映射规则配置错误:将路径写为/foo是精确匹配规则,仅会匹配/foo这一个请求路径,子路径资源访问、目录遍历逻辑都不会触发,需要配置为前缀匹配/foo/*才能覆盖路径下所有资源。
  • 缺少关键参数pathInfoOnly=true:没有这个参数时,DefaultServlet会把完整请求路径拼接到资源查找路径后,会去查找/tmp/foo目录而非/tmp目录,路径不存在自然返回404。另外给自定义映射设置default=true会覆盖容器默认的DefaultServlet,会导致原有静态资源、错误页处理逻辑失效。
  • 额外注意:不要给自定义DefaultServlet设置name="default",这个名称是Jetty内置默认Servlet的保留名称,重名会导致覆盖原有Servlet配置,引发不可预期的问题。

正确实现代码

public class MyJettyWebServerCustomizer implements WebServerFactoryCustomizer<JettyServletWebServerFactory> {

    @Override
    public void customize(JettyServletWebServerFactory factory) {
        factory.addServerCustomizers(server -> {
            WebAppContext webAppContext = (WebAppContext) server.getChildHandlersByClass(WebAppContext.class)[0];
            // 新建独立的DefaultServlet实例,不要复用容器已初始化的默认Servlet
            ServletHolder tmpDirServlet = new ServletHolder("tmp-dir-list", DefaultServlet.class);
            tmpDirServlet.setInitParameter("dirAllowed", "true");
            // 配置为仅处理路径后缀部分,避免把/foo拼接到资源路径后
            tmpDirServlet.setInitParameter("pathInfoOnly", "true");
            tmpDirServlet.setInitParameter("resourceBase", "/tmp/");
            tmpDirServlet.setInitOrder(0);
            // 使用前缀匹配规则映射路径
            webAppContext.addServlet(tmpDirServlet, "/foo/*");
        });
    }
}

配置完成后启动服务,访问http://localhost:8080/foo即可看到/tmp目录下的文件列表,访问/foo/xxx会自动映射到/tmp/xxx对应的文件或子目录。

注意:请确保Jetty运行进程对配置的/tmp目录有读取和遍历权限,Windows环境下路径需要调整为系统对应格式,比如C:/tmp/,使用正斜杠避免转义问题。

内容的提问来源于stack exchange,提问作者Stefan Ziegler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 14:51:11