问题描述

我的 WebContent/jsps 文件夹中的 JSP 文件中有一个 HTML 表单。我在 src 文件夹的默认包中有一个 servlet 类 servlet.java 。在我的 web.xml 中,它被映射为/servlet

我在 action 的 HTML 表单中尝试了几个 URL:

<form action="/servlet">
<form action="/servlet.java">
<form action="/src/servlet.java">
<form action="../servlet.java">

但没有一个工作。他们都不断返回 HTTP 404 错误,如下所示:

HTTP Status 404

The requested resource (/servlet) is not available.

为什么不起作用?

最佳解决方案

将 servlet 类放在 package

首先,将 servlet 类放在 Java package 中。您应该始终将公开可重用的 Java 类放在一个包中,否则它们对于包中的类 (如服务器本身) 是不可见的。这样就可以消除潜在的 environment-specific 问题。无包 Servlet 仅在特定的 Tomcat + JDK 组合中工作,这绝对不会被依赖。

在”plain” IDE 项目的情况下,该类需要放置在”Java Resources” 文件夹中的包结构中,因此不需要”WebContent”,这适用于 JSP 等 Web 文件。以下是 “导航器” 视图中所示的默认 Eclipse 动态 Web 项目的文件夹结构示例:

EclipseProjectName
 |-- src
 |    `-- com
 |         `-- example
 |              `-- YourServlet.java
 |-- WebContent
 |    |-- WEB-INF
 |    |    `-- web.xml
 |    `-- jsps
 |         `-- page.jsp
 :

在 Maven 项目的情况下,需要将类放置在 main/java 内的包装结构中,因此不需要。 main/resources,这是用于 non-class 文件。以下是 Eclipse Navigator 视图中所示的默认 Maven webapp 项目的文件夹结构示例:

MavenProjectName
 |-- src
 |    `-- main
 |         |-- java
 |         |    `-- com
 |         |         `-- example
 |         |              `-- YourServlet.java
 |         |-- resources
 |         `-- webapp
 |              |-- WEB-INF
 |              |    `-- web.xml
 |              `-- jsps
 |                   `-- page.jsp
 :

请注意,/jsps 子文件夹不是必需的。你甚至可以没有它,并将 JSP 文件直接放在 webcontent /webapp root 中,但是我只是从你的问题中接手。

url-pattern 中设置 servlet URL

servlet URL 被指定为 servlet 映射的”URL pattern” 。 servlet 类的 classname /filename 绝对不是每个定义。 URL 模式将被指定为 @WebServlet 注释的值。

package com.example; // Use a package!

@WebServlet("/servlet") // This is the URL of the servlet.
public class YourServlet extends HttpServlet { // Must be public and extend HttpServlet.
    // ...
}

如果您想支持像/servlet/foo/bar 这样的路径参数,请改用/servlet/*的 URL 格式。另见 Servlet and path parameters like /xyz/{value}/test, how to map in web.xml?

@WebServlet 仅适用于 Servlet 3.0 或更高版本

为了使用 @WebServlet,您只需确保您的 web.xml 文件 (如果有的话 (Servlet 3.0 中是可选的)) 声明为符合 Servlet 3.0+版本,因此不符合例如。 2.5 版本或更低版本。下面是一个 Servlet 3.1 兼容的 (它匹配 Tomcat 8+,WildFly 8+,GlassFish 4+等) 。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    id="WebApp_ID" version="3.1"
>
    <!-- Config here. -->
</web-app>

或者,如果您不是 Servlet 3.0+(不是 Tomcat 7 或更新版本,而是 Tomcat 6 或更早版本),请删除 @WebServlet 注释。

package com.example;

public class YourServlet extends HttpServlet {
    // ...
}

并在 web.xml 中注册 servlet,如下所示:

<servlet>
    <servlet-name>yourServlet</servlet-name>
    <servlet-class>com.example.YourServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>yourServlet</servlet-name>
    <url-pattern>/servlet</url-pattern>  <!-- This is the URL of the servlet. -->
</servlet-mapping>

请注意,您不应该同时使用这两种方法。使用基于注释的配置或基于 XML 的配置。

验证构建/部署

如果您使用的是 Eclipse 和/或 Maven 等构建工具,则需要确保已编译的 servlet 类文件位于生成的 WAR 文件的/WEB-INF/classes 文件夹中的包结构中。否则您将面临 HTTP 500 错误,如下所示:

HTTP Status 500

Error instantiating servlet class com.example.YourServlet

并在服务器日志中找到一个 java.lang.ClassNotFoundException: com.example.YourServlet,其次是 java.lang.NoClassDefFoundError: com.example.YourServlet,然后是 javax.servlet.ServletException: Error instantiating servlet class com.example.YourServlet

验证 servlet 是否正确编译并放置在 classpath 中的简单方法是让构建工具生成一个 WAR 文件 (例如,右键单击项目,Eclipse 中的 Export> WAR 文件),然后使用 ZIP 工具检查其内容。如果/WEB-INF/classes 中缺少 servlet 类,则该项目配置不正确或某些 IDE /项目配置默认值被错误地还原 (例如,在 Eclipse 中 “自动生成” 已被禁用) 。如果您没有线索,最好是从头开始重新启动,不要触摸任何 IDE /项目配置默认值。

单独测试 servlet

如果服务器在 localhost:8080 上运行,并且 WAR 成功部署在/contextname 的上下文路径上 (默认为 IDE 项目名称,区分大小写),并且 servlet 未初始化失败 (读取任何服务器日志部署/servlet 成功/失败消息和实际的上下文路径和 servlet 映射),则/servlet 的 URL 模式的 servlet 可以在 http://localhost:8080/contextname/servlet 获得。

您可以直接在浏览器的地址栏输入,以便对其进行测试。如果 doGet()被正确覆盖并实现,那么您将在浏览器中看到它的输出。或者如果您没有任何 doGet(),或者如果它不正确地调用 super.doGet(),那么将显示一个 “HTTP 405: HTTP method GET is not supported by this URL” 错误 (这比 404 更好,因为 405 是证实 servlet 本身被实际发现的证据) 。

覆盖 service()是一个不好的做法,除非你重新创建一个 MVC 框架 – 这是不太可能的,如果你刚刚开始使用 servlet,并且对于当前问题中描述的问题是无知的;) 另请参见 Design Patterns web based applications

无论如何,如果 servlet 已经通过 invidivually 测试返回 404,那么用 HTML 表单来尝试一下就完全没有意义了。从逻辑上来说,在 servlet 的 404 错误问题中包含任何 HTML 表单也是毫无意义的。

从 HTML 引用 servlet URL

一旦验证了 Servlet 在单独调用时工作正常,那么您可以前进到 HTML 。对于 HTML 表单的具体问题,<form action> 值需要是一个有效的 URL 。同样适用于<a href> 。您需要了解绝对/相对 URL 的工作原理。你知道,一个 URL 是一个网址,可以在 webbrowser 的地址栏中输入/查看。如果您将相对 URL 指定为表单操作,即没有使用 http://方案,则它将与您在 Webbrowser 的地址栏中看到的当前 URL 相关。因此绝对不会像服务器的 WAR 文件夹结构中的 JSP /HTML 文件位置那么多,因为许多启动程序似乎都认为。

因此,假设具有 HTML 表单的 JSP 页面由 http://localhost:8080 /contextname /jsps /page.jsp 打开,您需要提交到位于 http://localhost:8080 /contextname /servlet,这里有几种情况 (请注意,您可以在这里安全地用< a href> 替换< form action>):

  • 表单操作提交到带有主要斜杠的 URL 。

    <form action="/servlet">
    

    主要斜杠/使 URL 相对于域,因此表单将提交到

    http://localhost:8080/servlet
    

    但是,由于错误的情况,这可能会导致 404 。

  • 表单操作提交到一个 URL,而不带领导斜杠。

    <form action="servlet">
    

    这使得 URL 相对于当前 URL 的当前文件夹,因此表单将提交给

    http://localhost:8080/contextname/jsps/servlet
    

    但这可能会导致 404,因为它在错误的文件夹。

  • 表单操作提交到一个文件夹的 URL 。

    <form action="../servlet">
    

    这将一个文件夹 (完全像本地磁盘文件系统路径!),因此表单将提交到

    http://localhost:8080/contextname/servlet
    

    这一个必须工作!

  • 然而,规范的方法是使 URL domain-relative,以便在碰巧将 JSP 文件移动到另一个文件夹时,不需要再次修复 URL 。

    <form action="${pageContext.request.contextPath}/servlet">
    

    这将产生

    <form action="/contextname/servlet">
    

    这将始终提交到正确的 URL 。

在 HTML 中使用直接引号

您需要确保您在 HTML 属性 (如 action="..."action='...') 中使用直接引号,因此不会像 action=”...”action=’...’那样 curl 引号。 HTML 中不支持 curl 引号,它们只会成为该值的一部分。

也可以看看:

HTTP 状态 404 的其他情况错误:

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。