JSP HTTP 状态码
在 JSP 里设置状态码、错误页配置、以及常见误用。
设置状态码
<%@ page contentType="text/html;charset=UTF-8" %>
<%
String id = request.getParameter("id");
if (id == null || id.isBlank()) {
response.sendError(400, "缺少 id 参数");
return;
}
if (!id.matches("\\d+")) {
response.sendError(400, "id 必须是数字");
return;
}
%>| Code | When |
|---|---|
| 200 | 正常返回 |
| 201 | 新建资源成功,可带 Location |
| 204 | 成功但没有响应体 |
| 301 / 302 | 永久 / 临时跳转 |
| 400 | 参数缺失或格式错 |
| 401 | 未登录 |
| 403 | 已登录无权限 |
| 404 | 资源不存在 |
| 500 | 未捕获异常 |
错误页
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/errors/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/errors/500.jsp</location>
</error-page><%@ page isErrorPage="true" contentType="text/html;charset=UTF-8" %>
<%
Object code = request.getAttribute("jakarta.servlet.error.status_code");
Object uri = request.getAttribute("jakarta.servlet.error.request_uri");
%>
<!DOCTYPE html>
<html><body>
<h1>出错了</h1>
<p>状态码:<%= code %></p>
<p>请求地址:<%= uri %></p>
</body></html>isErrorPage="true" does two things: it exposes the exception object, and it tells the container this page is an error page so it is not wrapped in another error handler (avoiding infinite recursion).
只在本页处理异常
<%@ page errorPage="/WEB-INF/errors/500.jsp" %>
<%
int n = Integer.parseInt(request.getParameter("n")); // 可能抛异常
%>errorPage says "where to go if this page fails"; isErrorPage says "I am the handler". The first goes on business pages, the second on error pages.
isErrorPage="true" without any errorPage sends exceptions to the container default page — still 500, but with Tomcat styling and version disclosure. Configure both.别用 200 表示失败
<%-- 错误:AJAX 调用方看到 200 会当成功处理 --%>
<% response.setStatus(200); %>
<p>操作失败:库存不足</p>
<%-- 正确 --%>
<% response.sendError(409, "库存不足"); %>A frontend fetch only enters catch on non-2xx. Returning 200 with an error string forces callers to parse prose to decide success — the most common API smell there is.
设置状态码
<%@ page contentType="text/html;charset=UTF-8" %>
<%
String id = request.getParameter("id");
if (id == null || id.isBlank()) {
response.sendError(400, "缺少 id 参数");
return;
}
if (!id.matches("\\d+")) {
response.sendError(400, "id 必须是数字");
return;
}
%>| 状态码 | 何时用 |
|---|---|
| OK | 正常返回 |
| Created | 新建资源成功,可带 Location |
| No Content | 成功但没有响应体 |
| 重定向 | 永久 / 临时跳转 |
| Bad Request | 参数缺失或格式错 |
| Unauthorized | 未登录 |
| Forbidden | 已登录无权限 |
| Not Found | 资源不存在 |
| Internal Error | 未捕获异常 |
错误页
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/errors/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/errors/500.jsp</location>
</error-page><%@ page isErrorPage="true" contentType="text/html;charset=UTF-8" %>
<%
Object code = request.getAttribute("jakarta.servlet.error.status_code");
Object uri = request.getAttribute("jakarta.servlet.error.request_uri");
%>
<!DOCTYPE html>
<html><body>
<h1>出错了</h1>
<p>状态码:<%= code %></p>
<p>请求地址:<%= uri %></p>
</body></html>isErrorPage="true" 有两个作用:能拿到 exception 隐式对象,并且容器知道这是一个错误页,不会在它上面再套一层错误处理(避免无限递归)。
只在本页处理异常
<%@ page errorPage="/WEB-INF/errors/500.jsp" %>
<%
int n = Integer.parseInt(request.getParameter("n")); // 可能抛异常
%>errorPage 是「本页出错时去哪」,isErrorPage 是「我是处理错误的页」。前者写在业务页,后者写在错误页,两个配合使用。
isErrorPage="true" 却忘了配 errorPage,异常会走到容器默认页——状态码还是 500,但页面是 Tomcat 的默认样式,暴露版本信息。两处都配才完整。别用 200 表示失败
<%-- 错误:AJAX 调用方看到 200 会当成功处理 --%>
<% response.setStatus(200); %>
<p>操作失败:库存不足</p>
<%-- 正确 --%>
<% response.sendError(409, "库存不足"); %>前端 fetch 只在非 2xx 时走 catch。返回 200 加一段错误文字,等于让调用方自己去解析自然语言判断成败——这是接口设计里最常见的坏味道。