介绍
这可能有很多原因,这些原因将在以下各节中分解:
- 将 servlet 类放在
package
- 在 中设置 servlet URL
url-pattern
-
@WebServlet
仅适用于 Servlet 3.0 或更高版本
-
javax.servlet.*
在 Servlet 5.0 或更高版本中不再工作
- 确保编译后的文件存在于构建的 WAR 中
*.class
- 单独测试 servlet,无需任何 JSP/HTML 页面
- 使用相对于域的 URL 从 HTML 引用 servlet
- 在 HTML 属性中使用直引号
将 servlet 类放在package
首先,将 servlet 类放在 Java 中。您应该始终将可公开重用的 Java 类放在包中,否则它们对于包中的类(如服务器本身)是不可见的。通过这种方式,您可以消除潜在的特定于环境的问题。无包 servlet 只能在特定的 Tomcat+JDK 组合中工作,这永远不应该被依赖。package
对于“普通”IDE 项目,类需要放在“Java Sources”文件夹内的包结构中,而不是放在“Web 内容”文件夹(用于 JSP 等 Web 文件)中。下面是在导航器视图中看到的缺省 Eclipse 动态 Web 项目的文件夹结构示例(默认情况下,“Java Sources”文件夹位于此类项目中,由 folder 表示):src
EclipseProjectName
|-- src
| `-- com
| `-- example
| `-- YourServlet.java
|-- WebContent
| |-- WEB-INF
| | `-- web.xml
| `-- jsps
| `-- page.jsp
:
在 Maven 项目的情况下,类需要放在其内部的包结构中,因此不是 ,这是针对非类文件,绝对不是,这是针对 Web 文件的。下面是默认 Maven webapp 项目的文件夹结构示例,如 Eclipse 的 Navigator 视图中所示:main/java
main/resources
main/webapp
MavenProjectName
|-- src
| `-- main
| |-- java
| | `-- com
| | `-- example
| | `-- YourServlet.java
| |-- resources
| `-- webapp
| |-- WEB-INF
| | `-- web.xml
| `-- jsps
| `-- page.jsp
:
请注意,子文件夹不是绝对必要的。你甚至可以不用它,把JSP文件直接放在webcontent/webapp根目录中,但我只是从你的问题中接管了这个。/jsps
在 中设置 servlet URLurl-pattern
Servlet URL 被指定为 servlet 映射的“URL 模式”。它绝对不是根据定义 servlet 类的类名/文件名。URL 模式将指定为注释值。@WebServlet
package com.example; // Use a package!
import jakarta.servlet.annotation.WebServlet; // or javax.*
import jakarta.servlet.http.HttpServlet; // or javax.*
@WebServlet("/servlet") // This is the URL of the servlet.
public class YourServlet extends HttpServlet { // Must be public and extend HttpServlet.
// ...
}
如果要支持路径参数,如 ,请改用 URL 模式。另请参阅 Servlet 和路径参数,如 /xyz/{value}/test,如何在 web 中映射.xml?/servlet/foo/bar
/servlet/*
@WebServlet
仅适用于 Servlet 3.0 或更高版本
为了使用 ,您只需要确保您的文件(如果有的话)(自 Servlet 3.0 以来它是可选的),被声明为符合 Servlet 3.0+ 版本,因此不符合例如 2.5 版本或更低版本。下面是Servlet 5.0兼容的一个(匹配Tomcat 10 +,WildFly 22 +(预览),GlassFish / Payara 6 +等)。@WebServlet
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<!-- Config here. -->
</web-app>
下面是Servlet 4.0兼容的一个(与Tomcat 9+,WildFly 11+,GlassFish/Payara 5+等相匹配)。
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
>
<!-- Config here. -->
</web-app>
或者,如果您还没有使用Servlet 3.0+(例如Tomcat 6或更早版本),请删除注释。@WebServlet
package com.example;
import javax.servlet.http.HttpServlet;
public class YourServlet extends HttpServlet {
// ...
}
并像这样注册 servlet:web.xml
<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 的配置。如果两者都有,则基于 XML 的配置将覆盖基于注释的配置。
javax.servlet.*
在 Servlet 5.0 或更高版本中不再工作
自Jakarta EE 9 / Servlet 5.0(Tomcat 10,TomEE 9,WildFly 22 Preview,GlassFish 6,Payara 6,Liberty 22等)以来,该软件包已重命名为软件包。javax.*
jakarta.*
换句话说,请确保您不会仅仅为了编译包而将不同服务器的JAR文件随机放入WAR项目中,例如tomcat-servlet-api-9.x.x.jar。这只会造成麻烦。完全删除它,然后从中编辑 servlet 类的导入javax.*
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
自
import jakarta.servlet.*;
import jakarta.servlet.annotation.*;
import jakarta.servlet.http.*;
如果您使用的是 Maven,您可以在以下答案中找到 Tomcat 10+、Tomcat 9-、JEE 9+ 和 JEE 8- 的正确声明示例:Tomcat 将 servlets 转换为 javax.servlet.Servlet 而不是 jakarta.servlet.http.HttpServlet。另一种方法是将服务器降级到旧版本,例如从Tomcat 10降级到Tomcat 9或更早版本,但这显然不是推荐的方法。pom.xml
确保编译后的文件存在于构建的 WAR 中*.class
如果您使用的是 Eclipse 和/或 Maven 等构建工具,则需要绝对确保编译后的 servlet 类文件位于其包结构中生成的 WAR 文件的文件夹中。如果是 ,它必须位于 中。否则,您还将面临404错误,或者出现HTTP 500错误的情况,如下所示:/WEB-INF/classes
package com.example; public class YourServlet
/WEB-INF/classes/com/example/YourServlet.class
@WebServlet
<servlet>
HTTP 状态 500
实例化 servlet 类 com.example.YourServlet 时出错
并在服务器日志中找到一个,后跟一个,后跟一个。java.lang.ClassNotFoundException: com.example.YourServlet
java.lang.NoClassDefFoundError: com.example.YourServlet
jakarta.servlet.ServletException: Error instantiating servlet class com.example.YourServlet
验证 servlet 是否正确编译并放置在类路径中的一种简单方法是让构建工具生成一个 WAR 文件(例如,右键单击项目,在 Eclipse 中导出> WAR 文件),然后使用 ZIP 工具检查其内容。如果 中缺少 servlet 类,或者导出导致错误,则项目配置不当,或者某些 IDE/项目配置默认值被错误地还原(例如,项目>在 Eclipse 中禁用了“自动生成”)。/WEB-INF/classes
您还需要确保项目图标没有指示生成错误的红叉。您可以在“问题”视图中找到确切的错误(“窗口>”显示视图>其他...“)。通常错误消息很好。如果您不知道,最好从头开始重新启动,不要触及任何IDE/项目配置默认值。如果您使用的是 Eclipse,您可以在 How do i import javax.servlet / jakarta.servlet API in my Eclipse project?
单独测试 servlet,无需任何 JSP/HTML 页面
如果服务器运行在 上,并且 WAR 已成功部署在 上下文路径上(默认为 IDE 项目名称,区分大小写!),并且 Servlet 的初始化没有失败(读取任何部署/servlet 成功/失败消息的服务器日志以及实际的上下文路径和 servlet 映射),则 URL 模式为 的 servlet 位于 。localhost:8080
/contextname
/servlet
http://localhost:8080/contextname/servlet
您可以直接在浏览器的地址栏中输入它,以对其进行单独测试。如果它被正确覆盖和实现,那么您将在浏览器中看到它的输出。或者,如果您没有任何错误调用,或者如果它错误地调用,则会显示“HTTP 405:此URL不支持HTTP方法GET”错误(这仍然比404更好,因为405是实际找到servlet本身的证据)。doGet()
doGet()
super.doGet()
重写是一种不好的做法,除非你正在重新发明一个MVC框架——如果你刚刚开始使用servlet,并且对当前问题中描述的问题一无所知,那么这是非常不可能的;)另请参见基于 Web 的设计模式应用程序。service()
无论如何,如果 servlet 在经过单独测试时已经返回 404,那么尝试使用 HTML 表单是完全没有意义的。因此,从逻辑上讲,在有关 servlet 的 404 错误的问题中包含任何 HTML 表单也是完全没有意义的。
使用相对于域的 URL 从 HTML 引用 servlet
一旦你验证了 servlet 在单独调用时工作正常,那么你可以前进到 HTML。至于HTML表单的具体问题,该值必须是有效的URL。这同样适用于 、 、 等。您需要了解绝对/相对 URL 的工作原理。您知道,URL是一个网址,您可以在网页浏览器的地址栏中输入/看到。如果您要将相对URL指定为表单操作,即没有方案,那么它将相对于当前URL,如您在Web浏览器的地址栏中看到的那样。因此,它绝对不是像许多初学者认为的那样相对于服务器WAR文件夹结构中的JSP / HTML文件位置。<form action>
<a href>
<img src>
<script src>
http://
因此,假设带有 HTML 表单的 JSP 页面由 打开(因此不是由 打开),并且您需要提交到 位于 中的 servlet,这里有几种情况(请注意,您可以在此处安全地用 、 、 等替换):http://localhost:8080/contextname/jsps/page.jsp
file://...
http://localhost:8080/contextname/servlet
<form action>
<a href>
<img src>
<script src>
-
表单操作将提交到带有前导斜杠的 URL。
<form action="/servlet">
前导斜杠使 URL 相对于域,因此表单将提交到/
http://localhost:8080/servlet
但这可能会导致404,因为它在错误的上下文中。
-
表单操作将提交到不带前导斜杠的 URL。
<form action="servlet">
这使得URL相对于当前URL的当前文件夹,因此表单将提交到
http://localhost:8080/contextname/jsps/servlet
但这可能会导致404,因为它位于错误的文件夹中。
-
表单操作提交到一个 URL,该 URL 向上显示一个文件夹。
<form action="../servlet">
这将向上移动一个文件夹(与本地磁盘文件系统路径完全相同!),因此表单将提交到
http://localhost:8080/contextname/servlet
这个必须工作!
-
但是,规范方法是使 URL 成为相对域,这样当您碰巧将 JSP 文件移动到另一个文件夹中时,就不需要再次修复 URL。
<form action="${pageContext.request.contextPath}/servlet">
这将生成
<form action="/contextname/servlet">
因此,这将始终提交到正确的URL。
在 HTML 属性中使用直引号
您需要绝对确保在 HTML 属性中使用直引号,例如 or,因此不是像 or 这样的卷引号。HTML中不支持花引号,它们只会成为值的一部分。从博客复制粘贴代码片段时要小心!众所周知,一些博客引擎,特别是Wordpress,默认情况下使用所谓的“智能引号”,因此也会以这种方式破坏代码片段中的引号。另一方面,与其复制粘贴代码,不如尝试自己键入代码。通过你的大脑和手指实际获取代码的另一个好处是,从长远来看,它会让你更好地记住和理解代码,也会让你成为一个更好的开发人员。action="..."
action='...'
action=”...”
action=’...’
另请参阅:
HTTP 状态 404 错误的其他情况: