Jetty:默认的Servlet上下文路径

5

我需要将Servlet(仅限Servlet,而不是处理器,因为某些原因)设置为可以使用war文件以外的文件。在这里https://dev59.com/NWkv5IYBdhLWcg3w4kt2#28735121上,我找到了以下解决方案:

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。为什么?如何使其在这种特定的上下文中工作?我使用jetty 9.2.15是出于同样的原因。
1个回答

12
DefaultServlet旨在查看contextPath后的请求URI。
在您的示例代码中,当您将servlet的url-pattern从/更改为/foo/*时,正在查找的结果文件现在包括/foo/部分。
换句话说,请求URI /css/main.css会将期望在磁盘上找到的文件(位于)./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应用程序的目录,则也可以实现。 这将需要您设置另一个DefaultServlet,该Servlet不参与ServletContext。 一个这样的设置示例是...
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-pattern。

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

有关整个pathInfo、请求URI、contextPath和以/*结尾的url-pattern的更多信息,请参见@30thh的有用答案

这个独立的DefaultServlet声明不参与ServletContext,库将无法通过ServletContext方法看到或访问该DefaultServlet的内容。然而,所有传入的HTTP客户端请求都可以通过该url-pattern轻松请求内容。


1
很遗憾,我不能多次点赞。感谢您提供详细的解释。 - Pavel_K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接