Servlet 异常处理
容器如何处理未捕获异常、web.xml 的异常映射、日志与用户友好提示。
Key Points未捕获的运行时异常让容器返回 500;用 error-page 映射到友好页面,异常细节只写日志不返给用户。
异常会走到哪里
- Servlet 内
try/catch自己处理 —— 最可控 - 抛给容器 —— 容器按
error-page找匹配页 - 没有匹配 —— 返回容器默认 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.异常会走到哪里
- Servlet 内
try/catch自己处理 —— 最可控 - 抛给容器 —— 容器按
error-page找匹配页 - 没有匹配 —— 返回容器默认 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>匹配顺序是「异常类型由具体到宽泛,最后才是状态码」。exception-type 优先于 error-code,所以精确类型写在前面更清楚。
错误页里能拿到什么
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(); } %>把
e.printStackTrace() 打到响应里是最常见的泄密点:堆栈会暴露类名、包结构、SQL、甚至文件绝对路径。生产环境只给用户一个错误编号,细节查日志。