我需要设置Servlet(由于某些原因,只有Servlet而不是处理程序)才能在war之外使用文件。在https://stackoverflow.com/a/28735121/5057736中,我找到了以下解决方案:

Server server = new Server(8080);

ServletContextHandler ctx = new ServletContextHandler();
ctx.setContextPath("/");

DefaultServlet defaultServlet = new DefaultServlet();
ServletHolder holderPwd = new ServletHolder("default", defaultServlet);
holderPwd.setInitParameter("resourceBase", "./src/webapp/");

ctx.addServlet(holderPwd, "/*");//LINE N
ctx.addServlet(InfoServiceSocketServlet.class, "/info");

server.setHandler(ctx);

此解决方案有效,这就是我需要的。但是,一旦将LINE N更改为ctx.addServlet(holderPwd, "/foo/*");,它就会停止工作。我尝试了“/ foo /”,“/ foo”,但是结果是一样的-我得到了not found。为什么?如何在特定背景下使用它?由于相同的原因,我使用码头9.2.15。

最佳答案

DefaultServlet设计为在contextPath之后查看请求URI。

在示例代码中,当您将servlet的url模式从/更改为/foo/*时,在磁盘上查找的结果文件现在包括/foo/部分。

换句话说,/css/main.css的请求URI导致它期望以./src/webapp/foo/css/main.css找到的文件(在磁盘上)

您的示例有一些缺陷。为您的ServletContextHandler建立一个空的资源库并不明智,因为ServletContext本身需要访问该配置值。

您可以通过删除...来解决此问题

holderPwd.setInitParameter("resourceBase", "./src/webapp/");

并改用ServletContextHandler.setBaseResource(Resource) ...

ctx.setResourceBase(Resource.newResource(new File("./src/webapp")));

这将允许以下ServletContext方法(由无数servlet库使用)也可以正常工作
  • String getRealPath(String path)
  • URL getResource(String path)
  • InputStream getResourceAsStream(String path)
  • Set<String> getResources(String path)

  • 最后,要使ServletContextHandler中的设置合理,您将在“默认url-pattern”上添加default Servlet名称,该名称恰好实现为DefaultServlet

    像这样:
    // Lastly, the default servlet for root content
    // It is important that this is added last.
    String defName = "default"; // the important "default" name
    ServletHolder holderDef = new ServletHolder(defName, DefaultServlet.class);
    holderDef.setInitParameter("dirAllowed","true");
    ctx.addServlet(holderDef,"/"); // the servlet spec "default url-pattern"
    

    现在,如果您还需要将请求URI /foo/*中的静态内容提供给不属于该Web应用程序的目录,则也可以这样做。
    这将要求您设置另一个不参与DefaultServletServletContext

    此设置的一个示例是...

    package jetty;
    
    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.server.ServerConnector;
    import org.eclipse.jetty.servlet.DefaultServlet;
    import org.eclipse.jetty.servlet.ServletContextHandler;
    import org.eclipse.jetty.servlet.ServletHolder;
    import org.eclipse.jetty.util.resource.PathResource;
    
    import java.io.File;
    import java.nio.file.Path;
    
    public class ManyDefaultServlet
    {
        public static void main(String[] args) throws Exception {
            Server server = new Server();
            ServerConnector connector = new ServerConnector(server);
            connector.setPort(8080);
            server.addConnector(connector);
    
            // The filesystem paths we will map
            Path homePath = new File(System.getProperty("user.home")).toPath().toRealPath();
            Path pwdPath = new File(System.getProperty("user.dir")).toPath().toRealPath();
    
            // Setup the basic application "context" for this application at "/"
            // This is also known as the handler tree (in jetty speak)
            ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
            context.setContextPath("/");
            context.setBaseResource(new PathResource(pwdPath));
            server.setHandler(context);
    
            // Fist, add special pathspec of "/home/" content mapped to the homePath
            ServletHolder holderHome = new ServletHolder("static-home", DefaultServlet.class);
            holderHome.setInitParameter("resourceBase",homePath.toUri().toASCIIString());
            holderHome.setInitParameter("dirAllowed","true");
            // Use request pathInfo, don't calculate from contextPath
            holderHome.setInitParameter("pathInfoOnly","true");
            context.addServlet(holderHome,"/foo/*"); // must end in "/*" for pathInfo to work
    
            // Lastly, the default servlet for root content
            // It is important that this is last.
            String defName = "default"; // the important "default" name
            ServletHolder holderDef = new ServletHolder(defName, DefaultServlet.class);
            holderDef.setInitParameter("dirAllowed","true");
            context.addServlet(holderDef,"/"); // the servlet spec "default url-pattern"
    
            server.start();
            server.join();
        }
    }
    

    这使用第二个DefaultServlet,仅使用该DefaultServlet的唯一资源库,并映射到以/*结尾的url模式。

    最后,告诉第二个DefaultServlet的init参数使用请求URI的pathInfo,而不是像通常那样在contextPath上拆分。

    有关整个pathInfo,请求URI,contextPath和以/*结尾的url模式有关的更多信息,请参见useful answer by @30thh

    该独立的DefaultServlet声明不参与ServletContext,并且库将无法通过DefaultServlet方法查看或访问该ServletContext中的内容。但是,所有传入的HTTP客户端请求都可以通过该url模式轻松地请求内容。

    09-05 11:23