Servlet 返回“HTTP 状态 404 请求的资源 (/servlet) 不可用”介绍HTTP 状态 500

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

我已经在HTML表单的属性中尝试了几个URL:action

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

但这些都不起作用。它们都不断返回HTTP 404错误,如下所示Tomcat 6/ 7 / 8:

HTTP Status 404 — /servlet

描述:请求的资源 (/servlet) 不可用。

或者如下图中的Tomcat 8.5 /9所示:

HTTP 状态 404 — 未找到

消息: /servlet

说明:源服务器未找到目标资源的当前表示形式,或者不愿意透露存在该表示形式

或者如下图10所示:

HTTP 状态 404 — 未找到

类型:状态报告

消息:请求的资源 (/servlet) 不可用

说明:源服务器未找到目标资源的当前表示形式,或者不愿意透露存在该表示形式

为什么它不起作用?


答案 1

介绍

这可能有很多原因,这些原因将在以下各节中分解:

  • 将 servlet 类放在package
  • 在 中设置 servlet URLurl-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/javamain/resourcesmain/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 +等)。@WebServletweb.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/classespackage 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.YourServletjava.lang.NoClassDefFoundError: com.example.YourServletjakarta.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/servlethttp://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.jspfile://...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 错误的其他情况:


答案 2

场景 #1:你不小心从命令行重新部署,而tomcat已经在运行

简短的回答:停止 Tomcat,删除目标文件夹、mvn 包,然后重新部署


场景 #2: request.getRequestDispatcher(“MIS_SPELLED_FILE_NAME.jsp”)

简短的回答:检查文件名拼写,确保大小写正确。


场景#3:类未找到异常(答案放在这里,因为:问题#17982240)(java.lang.ClassNotFoundException for servlet in tomcat with eclipse)(被标记为重复并指导我到这里)

简答 #3.1:web.xml在 servlet 类标记中具有错误的包路径。

简答#3.2:java文件有错误的导入语句。


以下是方案 #1 的更多详细信息:


1:停止雄猫

  • 选项 1:在终端中通过 CTRL+C。
  • 选项2:(在雄猫仍在运行时关闭终端)
  • ------------ 2.1: 按:Windows+R --> 类型:“services.msc"
  • ------------ 2.2:在列表的“名称”列中找到“Apache Tomcat #.# Tomcat#”。
  • ------------ 2.3:右键单击 --> “停止”"

2:删除“目标”文件夹。(mvn清洁不会在这里帮助你)

3:mvn 包

4: YOUR_DEPLOYMENT_COMMAND_HERE

(我的:java -jar target/dependency/webapp-runner.jar --port 5190 target/*.war)

完整背景故事:


意外地打开了一个新的git-bash窗口,并尝试通过以下方式为我的heroku项目部署一个.war文件:

java -jar target/dependency/webapp-runner.jar --port 5190 target/*.war

在部署失败后,我意识到我打开了两个git-bash窗口,并且没有使用CTLR + C来停止以前的部署

我遇到了:

HTTP 状态 404 – 未找到类型状态报告

消息 /if-student-test.jsp

说明 源服务器未找到目标资源的当前表示形式,或者不愿意透露存在该表示形式。

Apache Tomcat/8.5.31

以下是方案 #3 的更多详细信息:


场景 3.1:Web.xml文件中的 servlet 类包路径错误。

它应该与 java servlet 类顶部的包语句匹配。

文件:my_stuff/MyClass.java

   package my_stuff;

文件: PRJ_ROOT/src/main/webapp/WEB-INF/web.xml

   <servlet-class>
   my_stuff.MyClass
   </servlet-class>

场景 3.2:

您将错误的“”语句放在 myClass.java 文件的顶部。

例如:

文件位于:“/my_stuff”文件夹中

你错误地写了:

package com.my_stuff

这很棘手,因为:

1:maven 构建(mvn 包)不会在此处报告任何错误。

2:web中的servlet类行.xml可以有正确的包路径。例如:

<servlet-class>
my_stuff.MyClass
</servlet-class>

使用堆栈:Notepad++ + GitBash + Maven + Heroku Web App Runner + Tomcat9 + Windows10


推荐