現象:代碼執行了 response.sendRedirect(url),控制台無報錯,但瀏覽器網址欄不變、頁面不跳轉。這通常由兩個核心原因導致。
sendRedirect 原理是發送 HTTP 302 響應頭(Location: url),瀏覽器收到後自動跳轉。一旦響應體開始輸出並刷新到客戶端(Committed),響應頭就無法修改,跳轉失效。
<%@ include ... %> 引入的文件有輸出。out.flush() 或緩衝區自動刷新(默認 8KB)。sendRedirect 前已有大量模板文本輸出。sendRedirect 不會終止當前線程,後續代碼照常運行,可能再次輸出內容、再次設置響應頭、甚至拋出異常,導致瀏覽器收到混亂響應。
<%@ page contentType="text/html;charset=UTF-8" %>
<%
// 1. 業務邏輯判斷
if (session.getAttribute("user") == null) {
// 2. 僅在響應未提交前調用
response.sendRedirect(request.getContextPath() + "/login.jsp");
// 3. 立即 return,阻斷後續執行
return;
}
%>
<!-- 正常頁面內容 -->
不要在每個 JSP 重複判斷,使用 Filter 或 Spring MVC HandlerInterceptor 集中處理:
@WebFilter("/*")
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String uri = request.getRequestURI();
// 靜態資源、登錄頁放行
if (uri.endsWith("login.jsp") || uri.contains("/static/")) {
chain.doFilter(req, res);
return;
}
if (request.getSession().getAttribute("user") == null) {
response.sendRedirect(request.getContextPath() + "/login.jsp");
// Filter 中無需 return,但必須不再調用 chain.doFilter
return;
}
chain.doFilter(req, res);
}
}
<%
System.out.println("isCommitted before redirect: " + response.isCommitted());
response.sendRedirect("/login.jsp");
System.out.println("isCommitted after redirect: " + response.isCommitted());
%>
若 before 為 true,說明在此之前已有輸出導致提交,需向上排查模板輸出點。
sendRedirect 前無任何模板文本、空白行、include 輸出。return(JSP/Servlet)或不再調用 chain.doFilter(Filter)。