Servlet 异常处理

容器如何处理未捕获异常、web.xml 的异常映射、日志与用户友好提示。

Intermediate
Key Points未捕获的运行时异常让容器返回 500;用 error-page 映射到友好页面,异常细节只写日志不返给用户。

异常会走到哪里

  1. Servlet 内 try/catch 自己处理 —— 最可控
  2. 抛给容器 —— 容器按 error-page 找匹配页
  3. 没有匹配 —— 返回容器默认 500 页,堆栈暴露给用户

在 Servlet 里处理

Java
@WebServlet("/user")
public class UserServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String id = req.getParameter("id");
        try {
            User u = userService.find(Integer.parseInt(id));
            if (u == null) {
                resp.sendError(HttpServletResponse.SC_NOT_FOUND, "用户不存在");
                return;
            }
            req.setAttribute("user", u);
            req.getRequestDispatcher("/WEB-INF/views/user.jsp").forward(req, resp);
        } catch (NumberFormatException e) {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "id 必须是整数");
        } catch (Exception e) {
            log("查询用户失败 id=" + id, e);       // 细节进日志
            throw new ServletException(e);         // 交给容器统一处理
        }
    }
}

web.xml 的异常映射

XML
<error-page>
  <exception-type>java.lang.NumberFormatException</exception-type>
  <location>/WEB-INF/errors/bad-request.jsp</location>
</error-page>
<error-page>
  <exception-type>java.sql.SQLException</exception-type>
  <location>/WEB-INF/errors/db-error.jsp</location>
</error-page>
<error-page>
  <error-code>500</error-code>
  <location>/WEB-INF/errors/500.jsp</location>
</error-page>

Matching goes from specific exception types to broad ones, then status codes. exception-type beats error-code, so list precise types first.

错误页里能拿到什么

JSP
<%@ page isErrorPage="true" contentType="text/html;charset=UTF-8" %>
<%
    Object status = request.getAttribute("jakarta.servlet.error.status_code");
    Object msg    = request.getAttribute("jakarta.servlet.error.message");
    Throwable ex  = (Throwable) request.getAttribute("jakarta.servlet.error.exception");
%>
<h1>出错了(<%= status %>)</h1>
<p><%= msg %></p>
<%-- 堆栈只写日志,不要输出到页面 --%>
<% if (ex != null) { ex.printStackTrace(); } %>
Printing e.printStackTrace() into the response is the most common leak: a stack trace exposes class names, package layout, SQL and even absolute paths. In production give the user an error id and keep details in the log.

Friends & Partners