為了提供友好的用戶體驗,生產環境絕不能讓堆疊追蹤直接暴露給瀏覽器。Servlet 規範提供了兩種互補的異常處理機制:聲明式(web.xml 配置)與程式化。本文通過完整範例演示如何處理 HTTP 錯誤碼(404、500)與 Java 異常類別,並展示如何用 Servlet 統一輸出錯誤頁面。
在 web.xml 中透過 <error-page> 元素映射錯誤碼或異常類別到處理資源(靜態 HTML、JSP 或 Servlet)。
<error-code>:HTTP 狀態碼(如 404、500)。<exception-type>:Java 異常全限定名(如 java.io.FileNotFoundException)。<location>:處理資源路徑(以 / 開頭,相對於 Web 應用根目錄)。FileNotFound.html
<html>
<head><meta charset="UTF-8"><title>頁面未找到</title></head>
<body>
你訪問的頁面並不存在,或已被移動。<p>
如有問題請聯繫 <a href="#">管理員</a>
</body>
</html>
web.xml
<error-page>
<error-code>404</error-code>
<location>/FileNotFound.html</location>
</error-page>
訪問不存在路徑(如 /index1.jsp),容器自動轉發到該 HTML。

當錯誤頁面需包含動態邏輯(記錄日誌、查詢資料庫、國際化)時,用 Servlet 處理更靈活。
ErrorHandlerServlet
public class ErrorHandlerServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=gb2312");
PrintWriter out = resp.getWriter();
Integer statusCode = (Integer) req.getAttribute("javax.servlet.error.status_code");
out.println("<html><head><title>錯誤列表</title></head><body>");
switch (statusCode) {
case 401:
out.println("<h2>HTTP 錯誤碼:" + statusCode + "</h2>");
out.println("你未被授權訪問此資源。");
break;
case 404:
out.println("<h2>HTTP 錯誤碼:" + statusCode + "</h2>");
out.println("頁面不存在或已移動。");
break;
}
out.println("如有問題請聯繫 <a href='#'>管理員</a>");
out.println("</body></html>");
out.close();
}
}
web.xml
<servlet>
<servlet-name>ErrorHandlerServlet</servlet-name>
<servlet-class>com.lp.servlet.ErrorHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ErrorHandlerServlet</servlet-name>
<url-pattern>/errorHandler</url-pattern>
</servlet-mapping>
<error-page>
<error-code>404</error-code>
<location>/errorHandler</location>
</error-page>
<error-page>
<error-code>401</error-code>
<location>/errorHandler</location>
</error-page>
容器會將錯誤屬性(javax.servlet.error.status_code、javax.servlet.error.exception 等)放入 request 屬性,Servlet 可取出動態生成頁面。

拋出異常的 Servlet
public class FileExceptionServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws FileNotFoundException {
File file = new File("1.txt"); // 故意不存在
new FileInputStream(file); // 拋出 FileNotFoundException
}
}
異常處理 Servlet
public class ExceptionHandlerServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=gb2312");
PrintWriter out = resp.getWriter();
out.println("<html><head><title>錯誤</title></head><body>");
out.println("應用程式出錯:伺服器端檔案可能被刪除。");
out.println("請聯繫 <a href='#'>管理員</a>");
out.println("</body></html>");
out.close();
}
}
web.xml
<servlet>
<servlet-name>ExceptionHandlerServlet</servlet-name>
<servlet-class>com.lp.servlet.ExceptionHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ExceptionHandlerServlet</servlet-name>
<url-pattern>/exceptionHandler</url-pattern>
</servlet-mapping>
<error-page>
<exception-type>java.io.FileNotFoundException</exception-type>
<location>/exceptionHandler</location>
</error-page>
直接訪問 /exceptionHandler 觸發異常,容器捕獲並轉發到處理 Servlet。

在業務邏輯中主動捕獲異常,記錄日誌,並呼叫 response.sendError(statusCode, message) 觸發容器錯誤頁面機制(可配合聲明式 error-page 顯示友好頁面)。
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
try {
File file = new File("1.txt");
new FileInputStream(file); // 可能拋出 FileNotFoundException
} catch (FileNotFoundException e) {
getServletContext().log(e.getMessage()); // 記錄到伺服器日誌
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "檔案未找到");
}
}
關鍵點:
sendError 會提交回應,後續不可再寫入輸出流。web.xml 配置了對應 error-code (500) 的 error-page,容器會顯示該頁面;否則顯示容器預設錯誤頁。ServletContext.log。
| 場景 | 推薦方式 | 說明 |
|---|---|---|
| 全站通用 HTTP 錯誤 (404, 500) | 聲明式 + 專用錯誤 Servlet | 集中維護,支援國際化、動態佈局 |
| 特定業務異常 (如檔案不存在、業務規則違反) | 程式化 try-catch + sendError / 自定義異常頁 | 可記錄詳細上下文,決定是否回滾事務 |
| 未捕獲的 RuntimeException | 聲明式 exception-type=java.lang.Throwable (放最後) | 作為最後防線,避免堆疊洩露 |
掌握這兩種機制的配合使用,能構建健壯、用戶友好的 Web 應用。現代框架(Spring MVC @ControllerAdvice、Spring Boot /error 端點)本質上是對此的進一步封裝,理解原理有助於排查框架層面的異常處理問題。