如何映射“根”Servlet以使其他脚本仍可运行?

57

我正在尝试构建一个Servlet,它调用类似以下JSP页面的内容:

public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException, ServletException {
    req.getRequestDispatcher("/WEB-INF/main.jsp").forward(req, resp);
}

我需要这个 Servlet 响应到域名的根目录(例如:http://example.com/),所以我在 web.xml 中使用以下映射:

<servlet-mapping>
    <servlet-name>MainServlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>
我遇到的问题是这个匹配了每一个URL,因此当分派器转发到“/WEB-INF/main.jsp”时,它会匹配url-pattern,因此Servlet再次运行。这导致循环运行,直到它因java.lang.StackOverflowError而终止。

如何匹配根目录而不影响其他脚本可运行?


你无法这样做,必须使用一些技巧。我曾在AppEngine上尝试过同样的事情,请参见此处:https://dev59.com/8XNA5IYBdhLWcg3wrf03 但是没有成功。 - Rahul Garg
3
也许是时候给出一个被认可的答案了吗? - Jeb
8个回答

49

使用空模式,例如:

<servlet-mapping>
    <servlet-name>MainServlet</servlet-name>
    <url-pattern></url-pattern>
</servlet-mapping>

servlet 3.0规范对此进行了澄清:

空字符串("")是一种特殊的URL模式,完全映射到应用程序的上下文根

因此,它至少应该在3.0容器中工作,并且我已经验证它可以在Jetty 8上工作。


1
应用引擎仍然停留在2.5版本,所以这并不令人意外。 - nilskp
@nilskp 那在App Engine上该怎么做呢?如果我把它留空,会显示一个错误。 - Ahmad Muzakki
@AhmadMuzakki 或许可以尝试 Jeb 的答案。 - nilskp
1
@nilskp 这适用于App Engine灵活环境,因为它运行Jetty 9.3(Servlet 3.1)。 - Eng.Fouad
1
在 WAS Liberty 上使用 Servlet-3.1 对我有效。 - lwestby
这是在Eclipse Neon J2EE项目中使用Tomcat9服务器的工作。非常感谢。"/"无法正常工作。 - Claod

29

使用 web.xml 的 welcome-file 元素解决了我的问题,在应用引擎上。以下是我的示例:

<web-app>
    <servlet>
        <servlet-name>RootServlet</servlet-name>
        <servlet-class>com.foo.RootServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>RootServlet</servlet-name>
        <url-pattern>/foo</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>foo</welcome-file>
    </welcome-file-list>
</web-app>

1
这个很好用。只要注意 'servlet-name' 不能与 'welcome-file' 相同即可。 - nunaxe
在Jetty或Tomcat上对我似乎不起作用。只有当欢迎文件是实际文件时,它才能正确打开。 - Jus12
在Apache Tomcat中运行得非常好,尽管@nilskp提供的解决方案有点轻 :) - user3856210
@Jus12 web.xml 中命名的任何文件都应该是一个已存在的文件,即使它被映射到一个 servlet(甚至是 faclets 文件)。 - Usagi Miyamoto
啊!这就是为什么我的欢迎文件没有起作用的原因!需要去掉“/”!所以,如果你的servlet url-pattern是/Blabla,那么你的欢迎文件应该是“Blabla”,不要带上“/”。太好了!最佳解决方案!谢谢! - Claod

7
原始问题没有提到他们正在尝试在App Engine上映射根servlet - 在Tomcat(以及我所知道的其他servlet容器)上很容易,但是App Engine不是一个普通的servlet容器。
我的常规方式是使用HttpServlet构建Web应用程序,添加一个具有标题、内容、错误、消息等的“页面”对象,并将输出转发到JSP模板。这在App Engine中工作起来非常困难。
  • JSP文件不能没有"/"开头的名称。
  • JSP文件不能位于子目录中
  • 无法使用“/”url-pattern将servlet映射到应用程序的根目录
这是我的web.xml(编辑过的简短版本),最终起作用了。
<web-app>
  <servlet>
    <!-- this servlet needs to redirect to a NamedDispatcher
         called "template" -->
    <servlet-name>Home</servlet-name>
    <servlet-class>my.domain.HomeSv</servlet-class>
  </servlet>
  <servlet>
    <!-- jsp file must apparently be in root directory and have "/" at
         start of path -->
    <servlet-name>template</servlet-name>
    <jsp-file>/template.jsp</jsp-file>
  </servlet>
  <servlet-mapping>
    <!-- map your home servlet to somewhere other than "/" -->
    <servlet-name>Home</servlet-name>
    <url-pattern>/home</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <!-- make your Home servlet the welcome file -->
    <welcome-file>home</welcome-file>
  </welcome-file-list>
</web-app>

我在验证这一切方面并不是特别科学 - 但现在它对我有效,我对此感到非常满意。


1
在开发服务器上,似乎添加一个回退<welcome-file>index.html</welcome-file>会导致该index.html优先于您的servlet(如果存在),即使它是在<welcome-file>home</welcome-file>之后指定的。 - hraban

4
您可以在根目录下创建一个名为index.jsp的欢迎文件,并使用JSTL或其他方式编写以下代码。
<c:redirect url="/main"/>

在web.xml文件中,您将会看到以下内容:
<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>        
</welcome-file-list>

所以任何请求根目录的请求都将被重定向到 /main。现在您的servlet可以映射到 main。
<servlet-mapping>
    <servlet-name>MainServlet</servlet-name>
    <url-pattern>/main</url-pattern>
</servlet-mapping>

2
好的,我明白这样做可以起作用,但感觉像是一个hack。难道真的不可能让一个servlet自己处理根目录吗? - Jeremy Logan
抱歉投反对票,但有更好的解决方案 - https://dev59.com/dnNA5IYBdhLWcg3wQ7i4#4443581 - Maxim Kachurovskiy
这种解决方案的优点是它不会在每个目录中创建映射,就像welcome-files那样。所以我使用了它(尽管用的是普通的<%脚本,而不是标签),我会投赞成票。 - Rob N

1

尝试仅从模式中删除“*”,即

<url-pattern>/</url-pattern>

@fiXedd 显然已经尝试过那个方法,但没有奏效。(请参见我的答案编辑历史记录。) 可能与在App Engine上运行有关... - Stu Thompson

1

1
感谢介绍这个urlrewrite给我。它帮助我的Java webapp与Durandal协同工作,因为我之前遇到了一些奇怪的问题,比如在URL前添加路由别名。 - Jake Gaston

0

你尝试过以下方法吗?(请注意缺少的*,这是通配符,也是您的配置捕获所有内容的原因。)

<servlet-mapping>
        <servlet-name>MainServlet</servlet-name>
        <url-pattern>/index.jsp</url-pattern>
</servlet-mapping>

(根据just /的评论进行了编辑。)


我做了...结果完全一样。我应该提到我是在App Engine上做这个。我开始觉得他们的映射有点奇怪。 - Jeremy Logan

0

你不能重定向到WEB-INF。Servlet容器永远不会为该文件夹中的文档提供服务请求。

如果您希望您的应用程序(不仅仅是一个servlet,而是整个应用程序)在根上下文(“/http://www.domainname.com/”)下可用,则需要为其设置上下文条目-而不是servlet映射。

使用Tomcat,您可以添加一个新的<Context> mapping(在大约3个不同的可能位置之一)。


1
我不是在做重定向,而是在做转发。这是内部的,所以它们是否公开可见并不重要,对吧?无论如何,将它们移出WEB-INF也没有帮助。我没有使用Tomcat,但我会研究一下上下文映射,谢谢。 - Jeremy Logan
当请求被转发时,容器将在WEB-INF下提供资源。这是一种有效且有用的站点结构范例。 - Alex

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