進行數據驗證前,我們先解決一個一直沒有影響我們編碼,但可能卻影響心情的小問題。
即沒有引用servlet而引發的錯誤警告:

該類為tomcat為我們提供的類,修復步驟如下:
bulidpath -> config buildpath -> Libraries -> add Library... -> Server Runtime -> Apache Tomcat v7.0 -> finish -> ok。
這時候, 紅色的小叉叉應該沒有了。
<hr />
下面,我們正式加入驗證信息。
流程圖如下:

## 定義出錯后的操作界面error
jsp/teacher/error.jsp
```
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>驗證錯誤</title>
</head>
<body>
<h2>驗證錯誤</h2>
</body>
</html>
```
## 配置路由
假設我們為新增數據的保存操作進行驗證,則我們配置保存操作的路由:
struts.xml
```
<!-- 保存數據 -->
<action name="save" class="com.mengyunzhi.javaee.action.teacher.Save">
<!-- 設置V層,相對于WebContent的絕對路徑 -->
<result name="success">/jsp/success.jsp</result>
<result name="error">/jsp/error.jsp</result>
<result name="input">/jsp/teacher/error.jsp</result>
</action>
```
> 有人問為什么要使用input,而不用其它的呢。原因是因為大家都用input,所以我們也用input能夠更好的和其它軟件工程師交流。
## 測試路由與V層
打開保存的類,然后注釋掉原返回值,新增返回input
com.mengyunzhi.javaee.action.teacher SaveAction
我們對用戶名的長度進行規定,并在V層輸出我們返回的錯誤信息。
```
...
// 錯誤信息
private String error;
public String getError() {
return error;
}
public String execute() {
if (this.username.length() < 4) {
error = "用戶名長度過短";
return input;
}
if (this.name.length() < 3) {
error = "姓名長度過短";
return input;
}
......
```
### V層顯示
```
<h2>驗證錯誤</h2>
<h3><s:property value="error" /></h3>
```
> V層只所以可以使用`error`這個變量,這由于在Action中,存在getError()方法.
### 測試:

## 增加多個驗證,并一次性輸出
流程圖如下:

### 觸發器
```
...
public class SaveAction extends TeacherAction {
...
if (username.length() < 4) {
addActionError("用戶名長度過短");
}
if (this.name.length() < 3) {
addActionError("姓名長度過短");
}
...
// 存在錯誤,則返回input
if (hasActionErrors()) {
return INPUT;
} else {
return SUCCESS;
}
...
```
> 我們間接的繼承了`ActionSupport`,而`addActionError`等函數,則正是`ActionSupport`中的方法。
### V層
```
<h2>驗證錯誤</h2>
<ul>
<s:iterator value="actionErrors" var="actionError" status="status">
<li><s:property value="#actionError" /></li>
</s:iterator>
</ul>
```
### 測試

總結:我們可以在繼承ActionSupport后,很簡單的實現了字段的驗證,這在實際的開發中,也是必要的。
示例代碼如下:
```
package com.mengyunzhi.javaee.action.teacher;
import java.util.Collection;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
public class SaveAction extends TeacherAction {
/**
* 創建時間:2017.1.16
*/
private static final long serialVersionUID = 1L;
private String username;
private String password;
private String sex;
private String name;
private String email;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
// 該execute方法將被自動調用, 方法的返回類型必須為String
public String execute() {
if (username.length() < 4) {
addActionError("用戶名長度過短");
}
if (this.name.length() < 3) {
addActionError("姓名長度過短");
}
// 實例化并加載數據庫配置文件
Configuration configuration = new Configuration().configure();
// 構造服務注冊對象
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
// 創建會話工廠(session factory))
SessionFactory sessionFactory = configuration
.buildSessionFactory(serviceRegistry);
// 創建會話(這里的session也是會話的意思,我們以前接觸的http中的session,處理的是用戶與服務器的對話)
Session session = sessionFactory.getCurrentSession();
// 開啟事務(使用緩沖池進行數據庫的連接)
Transaction transaction = session.beginTransaction();
// 在這里,必須使用try catch finally語句。來確定會話正常關閉.
// 否則,當操作數據庫產生錯誤時,你可能需要重啟mysql服務
try {
// 初例化 賦值
com.mengyunzhi.javaee.entity.Teacher teacher = new com.mengyunzhi.javaee.entity.Teacher();
teacher.setName(name);
teacher.setUsername(username);
teacher.setEmail(email);
teacher.setPassword(password);
// 由于sex的類型是String對象,所以需要使用equals()來判等
if (sex.equals("0")) {
teacher.setSex(false);
} else {
teacher.setSex(true);
}
session.save(teacher);
// 提交事務
transaction.commit();
// 捕獲異常
} catch (HibernateException e) {
} finally {
// 如果session處于開啟狀態,則關閉session
if (session.isOpen()) {
// 關閉會話
session.close();
}
}
// 存在錯誤,則返回input
if (hasActionErrors()) {
return INPUT;
} else {
return SUCCESS;
}
}
}
```
> 注意:上述代碼未重構,直接在C層調用實體層,是錯誤的做法。我們將在下一個班級管理中,對該方法進行重構.
<hr />
下面,我們共同看看Struts使用注解進行驗證的方法。
我們在前面學過了,數據由用戶發送的時候,直接調用了action類的setXXX方法,來進行賦值。Struts的驗證也是利用了這一點,在進行賦值時,進行驗證。如果正確的則正確執行,如果錯誤,則返回"input".
流程圖如下:

# 內置驗證
首先,我們刪除在execute中用于驗證的代碼,并直接返回SUCCESS.
然后,我們在setName()、setUsername()方法上添加注解,來實現驗證.
```
...
@RequiredStringValidator(
message="用戶名不能為空")
@StringLengthFieldValidator(
minLength="4",
maxLength="8",
trim=true,
message="用戶名必須介于4-8之間")
public void setUsername(String username) {
this.username = username;
}
...
@RequiredStringValidator(
message="姓名不能為空")
@StringLengthFieldValidator(
minLength="2",
trim=true,
message="姓名不能少于2位")
public void setName(String name) {
this.name = name;
}
```
## V層顯示錯誤信息
驗證字段產生的錯誤將自動存儲在`fieldErrors`中,該字段同樣位于`ActionSupport`中,同時為我們提供了`getFieldErrors`供我們在前臺調用。
```
<ul>
<!-- 循環輸出字段驗證發生的錯誤 -->
<s:iterator value="fieldErrors" var="fieldError">
<li><s:property /></li>
</s:iterator>
</ul>
```

不止如此,我們還可以使用struts的內置標簽來快捷的輸出錯誤:
```
<body>
<h2>驗證錯誤</h2>
<ul>
<!-- 循環輸出字段驗證發生的錯誤 -->
<s:iterator value="fieldErrors" var="fieldError">
<li><s:property /></li>
</s:iterator>
</ul>
<!-- 使用struts內置標題輸出字段,將自動帶入出錯信息 -->
<s:textfield label="姓名:" name="name" />
<br />
<s:textfield label="用戶名:" name="username" />
</body>
```

好像,如果出現錯誤的話,應該再顯示add界面,并在add界面顯示錯誤吧。
<hr />
沒錯,的確是這樣,現在,讓我們重構V層。
1. 修改路由
```
<!-- 保存數據 -->
<action name="save" class="com.mengyunzhi.javaee.action.teacher.Save">
<!-- 設置V層,相對于WebContent的絕對路徑 -->
<result name="success">/jsp/success.jsp</result>
<result name="error">/jsp/error.jsp</result>
<result name="input">/jsp/teacher/add.jsp</result>
</action>
```
2. 重構add.jsp
```
<form action="save" method="post">
<s:textfield label="姓名:" name="name" />
<br />
<s:textfield label="用戶名:" name="username" />
<br /> 性別:<select name="sex">
<option value="0">男</option>
<option value="1">女</option>
</select> <br /> 郵箱:<input type="text" name="email" /><br /> 密碼:<input
type="password" name="password" /><br />
<button type="submit">submit</button>
</form>
```
3. 刪除jsp/teacher/error.jsp

作業:驗證其它3個字段;增加udpate的驗證。
> 官方文檔:[http://127.0.0.1:8081/docs/validation.html](http://127.0.0.1:8081/docs/validation.html), [http://127.0.0.1:8081/docs/validation-annotation.html](http://127.0.0.1:8081/docs/validation-annotation.html)
- README
- 第一章:準備
- 第二章:Hello World!
- 第一節:查看工程文件
- 第二節:JDK、JRE與環境變量
- 第三節:index.jsp
- 第三章:Hello Struts
- 第一節:Web.xml
- 第二節:單入口
- 第三節:Hello Struts
- 第四節:觸發C層
- 第四章:建立數據表
- 第一節:建立實體類
- 第二節:測試一
- 第三節:測試二
- 第四節:引入Hibernate
- 第五節:配置Hibernate
- 第六節:建立連接
- 第七節:實體類映射數據表
- 第八節:完善數據表
- 第五章:教師管理
- 第一節:增加數據--add
- 第二節:增加數據--save
- 1 獲取傳入數據數據
- 2 數據寫入測試
- 3 對接C層
- 第三節:數據列表
- 1 獲取數據
- 2 重構代碼
- 3 C層對接--初始化
- 4 C層添加數據
- 5 V層顯示數據
- 6 獲取數據庫中數據
- 7 顯示性別
- 8 分頁
- 9 條件查詢
- 第四節:修改數據
- 1 edit
- 2 update
- 第五節:刪除數據
- 第六節:總結
- 第六章:重構C層
- 第一節:繼承ActionSupport類
- 第二節:數據驗證
- 第七章:前臺分離(前臺)
- 第一節:環境搭建
- 第二節:運行環境
- 第三節:共享開發環境
- 第四節:生產環境
- 第八章:前臺開發(前臺)
- 第一節:本地化
- 第二節:教師列表
- 1 引入M層
- 2 模擬后臺返回數據
- 3 C與M對接
- 4 C與V對接
- 第九章:前后臺對接(前后臺)
- 第一節:后臺輸出json(后臺)
- 第二節:對接前臺(全棧)
- 第二節:對接API(前臺)
- 第二節:跨域請求(后臺)
- 第三節:重構代碼(前臺)
- 第十章:重構后臺M層
- 第一節:數據訪問DAO層
- 第二節:項目整體重構
- 第十一章:用戶登陸(前后臺)
- 第一節:制定規范
- 第二節:定制測試用例
- 第三節:后臺輸入測試代碼(后臺)
- 第四節:postman(后臺)
- 第五節:新建用戶登陸模塊(前臺)
- 第六節:代碼重構(前臺)
- 第十二章:班級管理(前后臺)
- 第一節:班級列表
- 1 原型開發
- 2 制定規范
- 3 后臺對接開發
- 4 前臺對接開發
- 第二節:Add
- 1 原型開發
- 2 制定規范
- 3 后臺對接開發
- 4 前臺對接開發
- 第三節:Save
- 1 制定規范
- 2 后臺對接開發
- 3 前臺對接開發
- 第四節:Edit
- 1 原型開發
- 2 制定規范
- 3 后臺對接開發
- 4 前臺對接開發
- 第五節:Update
- 1 制定規范
- 2 后臺對接開發
- 3 前臺對接開發
- 第六節:Delete
- 1 制定規范
- 2 后臺對接開發
- 3 前臺對接開發
- 第七節:小結
- 第十三章:班級管理(API)
- 第一節:ER圖
- 第二節:create
- 1 實體層
- 2 dao層
- 3 service(server)層
- 4 action層
- 第三節:ManyToOne
- 第四節:Read
- 1 service(server)層
- 2 action層
- 第五節:update
- 1 service(server)層
- 2 action層
- 第六節:update
- 第十四章:重構服務層