問題描述

我的 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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。