首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> JSP 程式設計 >> 關於JSP

JSP 異常處理全方位指南:、頁面指令、web.xml 三層防護體系

     編輯:關於JSP

異常處理在 Web 應用中的分層策略

JSP/Servlet 應用的異常處理應遵循「就近處理、分層攔截、統一展示」原則:

  1. 代碼塊級:針對特定片段(如第三方組件調用、非核心功能)局部捕獲,不影響主流程。
  2. 頁面級:單個 JSP 頁面發生未捕獲異常時,轉發至專用錯誤頁,保持用戶體驗連貫。
  3. 應用級:全局配置異常類型到錯誤頁映射,捕獲 Controller、Filter、Servlet 初始化等非 JSP 異常。

層級一: 標籤 —— 代碼塊級精準捕獲

JSTL 核心標籤庫提供 ``,類似 Java `try-catch`,僅捕獲其體內容拋出的 `Throwable`。

基本語法

<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>

<c:catch var="exception">
    <!-- 可能拋出異常的代碼:EL 表達式、標籤、Scriptlet -->
    <c:out value="${user.profile.address}" />
    <%= riskyHelper.generateReport() %>
</c:catch>

<c:if test="${exception != null}">
    <div class="alert warning">
        局部功能異常:${exception.message}
        <c:if test="${pageContext.request.isUserInRole('admin')}">
            <pre>${exception.stackTrace}</pre>
        </c:if>
    </div>
</c:if>

適用場景

  • 非關鍵區塊:廣告位、推薦模組、第三方 Widget、統計埋點。
  • EL 表達式訪問可能為 null 的巢狀屬性 (避免 `NullPointerException` 導致整頁 500)。
  • 包含 Scriptlet 的歷史代碼片段隔離。

注意事項

  • `var` 變量作用域為 `page`,僅在 `` 結束後可用。
  • 捕獲的是 `java.lang.Throwable`,包含 `Error` (如 `OutOfMemoryError`),生產環境建議在 `` 中判斷 `exception instanceof Exception` 再展示。
  • 不能捕獲 JSP 編譯期錯誤、指令錯誤、標籤庫加載失敗。

層級二:頁面指令 errorPage / isErrorPage —— 頁面級優雅降級

1. 定義錯誤處理頁

<%@ page contentType="text/html;charset=UTF-8" isErrorPage="true" %>
<!DOCTYPE html>
<html>
<head><title>系統錯誤</title></head>
<body>
    <h1>頁面加載失敗</h1>
    <p>發生時間:${pageContext.errorData.timestamp}</p>
    <p>請求 URI:${pageContext.errorData.requestURI}</p>
    <p>異常類型:${pageContext.errorData.throwable.class.name}</p>
    <p>異常信息:${pageContext.errorData.throwable.message}</p>
    <c:if test="${pageContext.request.isUserInRole('admin')}">
        <details><summary>堆疊追蹤</summary>
            <pre>${pageContext.errorData.throwable.stackTrace}</pre>
        </details>
    </c:if>
    <a href="javascript:history.back()">返回上一頁</a> | 
    <a href="${pageContext.request.contextPath}/">回首頁</a>
</body>
</html>

關鍵點:`isErrorPage="true"` 使隱含對象 `exception` (即 `pageContext.errorData.throwable`) 可用。

2. 業務頁面指定錯誤頁

<%@ page contentType="text/html;charset=UTF-8" errorPage="/error/generic.jsp" %>
<!-- 或者動態指定 (罕用) -->
<% response.setStatus(500); %>

當該 JSP 執行過程中拋出未捕獲異常,容器自動轉發至 `/error/generic.jsp`,原請求屬性保留,URL 不變 (服務端轉發)。

3. 錯誤頁隱含對象詳解

對象類型說明
exceptionThrowable拋出的異常實例 (僅 isErrorPage=true 可用)
pageContext.errorDataErrorData封裝異常、請求 URI、Servlet 名、時間戳
requestHttpServletRequest原請求,保留參數、屬性、Session
responseHttpServletResponse響應對象,狀態碼已設為 500

層級三:web.xml —— 應用級全局攔截

部署描述符配置異常類型、HTTP 狀態碼到錯誤頁的映射,覆蓋所有 Servlet、Filter、JSP、靜態資源請求。

配置範例

<web-app xmlns="https://xmlns.jcp.org/xml/ns/javaee" version="4.0">
    <!-- 按異常類型映射 (支援繼承關係) -->
    <error-page>
        <exception-type>java.lang.NullPointerException</exception-type>
        <location>/error/null-pointer.jsp</location>
    </error-page>
    <error-page>
        <exception-type>com.shop.exception.BizException</exception-type>
        <location>/error/business.jsp</location>
    </error-page>
    <error-page>
        <exception-type>java.lang.Throwable</exception-type>
        <location>/error/system.jsp</location>
    </error-page>

    <!-- 按 HTTP 狀態碼映射 -->
    <error-page>
        <error-code>404</error-code>
        <location>/error/404.jsp</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
        <location>/error/500.jsp</location>
    </error-page>
    <error-page>
        <error-code>403</error-code>
        <location>/error/403.jsp</location>
    </error-page>
</web-app>

匹配優先級規則

  1. 精確異常類型匹配 (最近繼承關係優先)。
  2. 父類異常類型匹配 (如 `Exception`、`Throwable`)。
  3. HTTP 狀態碼匹配 (僅當無異常類型匹配或異常未導致狀態碼設置時)。

Servlet 3.0+ 註解配置 (替代 web.xml)

@WebServlet(value = "/app/*", 
    initParams = @WebInitParam(name = "errorPage", value = "/error/system.jsp"))
public class AppServlet extends HttpServlet { ... }

// 或在 ServletContainerInitializer 中程序化註冊
context.addServlet("app", AppServlet.class)
       .setErrorPages(Collections.singleton(new ErrorPage(Exception.class, "/error/system.jsp")));

三層防護協同工作流程圖

請求進入
    │
    ├─► Filter 鏈 (編碼、登入檢查、日誌)
    │       │
    │       ├─ 異常 → web.xml error-page (403/500/Exception)
    │       │
    │       ▼
    ├─► Servlet / Controller
    │       │
    │       ├─ 捕獲異常 → 業務邏輯處理 / 拋出 BizException
    │       │
    │       ▼
    ├─► JSP 頁面渲染
    │       │
    │       ├─  捕獲局部異常 → 就近展示降級內容
    │       │
    │       ├─ 未捕獲異常 → page指令 errorPage 轉發
    │       │
    │       ▼
    └─► 響應輸出
            │
            └─ 容器級異常 (如 JSP 編譯錯誤、Filter 初始化失敗) → web.xml error-page

最佳實踐清單

  1. 自定義異常體系:`BizException` (業務邏輯錯誤,友好提示)、`SysException` (系統錯誤、三方調用失敗)、`NotFoundException` (資源不存在 → 404)。
  2. 統一錯誤頁風格:提取公共片段 (``),保持品牌一致性。
  3. 日誌記錄:錯誤頁中記錄 `ERROR` 級日誌 (含請求 ID、用戶 ID、堆疊),便於排查;避免在 `` 中重複記錄。
  4. 敏感信息過濾:生產環境錯誤頁**絕不**顯示堆疊追蹤、SQL、配置信息,僅記錄日誌。
  5. 監控告警:接入 APM (SkyWalking/Pinpoint/Zipkin) 或日誌平台 (ELK/Loki),對 500 錯誤率、特定異常類型設置告警。
  6. 測試覆蓋:單元測試 Service 異常拋出、集成測試 Controller 異常映射、E2E 測試錯誤頁展示。

常見誤區

  • 在 `errorPage` 目標頁又設置 `errorPage` → 循環轉發導致 `StackOverflowError`。
  • 錯誤頁中再次拋出異常且無更高層捕獲 → 容器顯示默認醜陋錯誤頁。
  • 使用 `response.sendRedirect("/error.jsp")` 代替轉發 → URL 變化、請求屬性丟失、狀態碼變為 302。
  • 捕獲 `Throwable` 後不重拋 `ThreadDeath` / `OutOfMemoryError` → 影響容器關閉鉤子、線程池關閉。

總結

JSP 異常處理三層體系:`` 精準隔離非核心塊、`errorPage` 頁面級優雅降級、`web.xml` 應用級全局托底。配合自定義異常體系、統一錯誤頁、日誌監控、敏感信息過濾,構建健壯、用戶友好、可運維的 Web 應用異常防護網。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved