「永遠不要信任客戶端輸入」是安全鐵律。本文系統梳理服務端驗證演進路線:**手工編碼 → Struts2 內建聲明式/編程式 → 自定義驗證器**,並給出最佳實踐代碼與避坑指南。
public String execute() {
if (StringUtils.isBlank(username)) {
addFieldError("username", "用戶名不能為空");
return INPUT;
}
if (!EmailValidator.getInstance().isValid(email)) {
addFieldError("email", "郵箱格式錯誤");
return INPUT;
}
return SUCCESS;
}
缺點:代碼分散、難複用、與業務邏輯耦合。
validation:執行驗證邏輯,收集 `FieldError` 到 `ActionContext`。workflow:檢查是否有錯誤,有則返回 `INPUT` 結果(默認),無則繼續執行。Action 必須實現 `ValidationAware` 接口(`ActionSupport` 已實現)。
| 名稱 | 類 | 典型參數 |
|---|---|---|
| required | RequiredFieldValidator | 非 null |
| requiredstring | RequiredStringValidator | trim+非空串 |
| int/long/short/double | XxxRangeFieldValidator | min/max |
| date | DateRangeFieldValidator | min/max (Date) |
| EmailValidator | -- | |
| url | URLValidator | -- |
| stringlength | StringLengthFieldValidator | minLength/maxLength/trim |
| regex | RegexFieldValidator | expression |
| expression | ExpressionValidator | expression (OGNL) |
| visitor | VisitorFieldValidator | context/prefix (級聯驗證嵌套對象) |
<!-- 1. 字段驗證:針對單字段多規則 -->
<validators>
<field name="age">
<field-validator type="int">
<param name="min">18</param>
<param name="max">60</param>
<message>年齡需在 ${min}-${max} 之間</message>
</field-validator>
<field-validator type="required">
<message>年齡必填</message>
</field-validator>
</field>
</validators>
<!-- 2. 純驗證器:跨字段或無字段綁定 -->
<validators>
<validator type="expression">
<param name="expression"><![CDATA[password == confirmPassword]]></param>
<message>兩次密碼不一致</message>
</validator>
</validators>
<!-- 3. 短路優化:同字段多規則,首個失效跳過後續 -->
<field-validator type="stringlength" short-circuit="true">...</field-validator>
問題:`conversionError` 攔截器在 `validation` 前執行,捕獲類型轉換異常但**不返回 INPUT**,導致錯誤信息丟失。
解決:項目 `src` 下建立同名包覆蓋 `conversionError` 攔截器,修改 `intercept()` 邏輯:捕獲異常 → `addFieldError` → 返回 `INPUT`。
public class UserAction extends ActionSupport {
@Override
public void validate() {
if (StringUtils.isBlank(username)) {
addFieldError("username", "用戶名不能為空");
}
if (age != null && (age < 18 || age > 60)) {
addFieldError("age", "年齡需在 18-60");
}
// 複雜業務規則:跨字段、查庫校驗等
if (userService.exists(username)) {
addFieldError("username", "用戶名已存在");
}
}
}
優點:Java 代碼完整表達力、易調試、可注入 Service。缺點:驗證邏輯與 Action 耦合。
Struts2 驗證體系提供「聲明式 XML、編程式 Java、自定義擴展」三層彈性。實戰中**簡單規則用 XML、複雜規則用 Java、通用規則做驗證器**,配合 `short-circuit`、類型轉換友好化、國際化,即可構建健壯、可維護的服務端防護牆。