本篇實作發布主題貼文頁面,包含表單驗證、登入攔截、錯誤回顯、CSRF 防護,採用 JSTL + EL 保持視圖純淨。
postForm。loginUser,未登入導向登入頁並保存 redirectUrl。postSubmit,Service 驗證後持久化,重導向列表頁。
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>
<html><head><title>發布主題</title>
<style>.error{color:red} textarea{width:100%;height:150px} .csrf{display:none}</style></head><body>
<h2>發布新主題</h2>
<form action="postSubmit" method="post">
<!-- CSRF Token -->
<input type="hidden" name="csrfToken" value="<c:out value='${sessionScope.csrfToken}'/>" class="csrf"/>
<input type="hidden" name="userId" value="<c:out value='${sessionScope.loginUser.id}'/>" />
<div>
<label>標題(必填):</label>
<input type="text" name="title" size="80" maxlength="200"
value="<c:out value='${param.title}'/>" required/>
<span class="error"><c:out value="${errors.title}" /></span>
</div>
<div>
<label>內容(必填):</label><br/>
<textarea name="content" required><c:out value="${param.content}"/></textarea>
<span class="error"><c:out value="${errors.content}" /></span>
</div>
<div>
<input type="submit" value="發布" />
<input type="button" value="取消" onclick="history.back()" />
</div>
</form>
</body></html>
required、maxlength 瀏覽器端首輪攔截,減少無效請求。${param.title}、${param.content},驗證失敗時保留輸入。errors Map,頁面 ${errors.title} 顯示。${sessionScope.loginUser.id} 直接取得,Controller 無需額外傳遞。<c:out> 自動轉義。@PostMapping("postSubmit")
public String postSubmit(HttpServletRequest request,
@RequestParam String title, @RequestParam String content) {
// 1. CSRF 驗證
String sessionToken = (String) request.getSession().getAttribute("csrfToken");
String formToken = request.getParameter("csrfToken");
if (sessionToken == null || !sessionToken.equals(formToken)) {
return "error/csrf"; // 或拋異常
}
// 2. 業務驗證
Map<String, String> errors = new HashMap<>();
if (title == null || title.trim().isEmpty()) errors.put("title", "標題不能為空");
if (title != null && title.length() > 200) errors.put("title", "標題不可超過 200 字");
if (content == null || content.trim().isEmpty()) errors.put("content", "內容不能為空");
if (!errors.isEmpty()) {
request.setAttribute("errors", errors);
return "postForm"; // 轉發回表單,保留 param 與 errors
}
// 3. 業務處理
User loginUser = (User) request.getSession().getAttribute("loginUser");
messageService.postTopic(loginUser.getId(), title.trim(), content.trim());
// 4. 重導向防重複提交
return "redirect:list";
}
下一篇將展示同功能的 Scriptlet 版本對比。