開發Web的一個原則就是,不能信任用戶輸入的任何信息,所以驗證和過濾用戶的輸入信息就變得非常重要,我們經常會在微博、新聞中聽到某某網站被入侵了,存在什么漏洞,這些大多是因為網站對于用戶輸入的信息沒有做嚴格的驗證引起的,所以為了編寫出安全可靠的Web程序,驗證表單輸入的意義重大。
我們平常編寫Web應用主要有兩方面的數據驗證,一個是在頁面端的js驗證(目前在這方面有很多的插件庫,比如ValidationJS插件),一個是在服務器端的驗證,我們這小節講解的是如何在服務器端驗證。
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/04.2.md#必填字段)必填字段
你想要確保從一個表單元素中得到一個值,例如前面小節里面的用戶名,我們如何處理呢?Go有一個內置函數`len`可以獲取字符串的長度,這樣我們就可以通過len來獲取數據的長度,例如:
~~~
if len(r.Form["username"][0])==0{
//為空的處理
}
~~~
`r.Form`對不同類型的表單元素的留空有不同的處理, 對于空文本框、空文本區域以及文件上傳,元素的值為空值,而如果是未選中的復選框和單選按鈕,則根本不會在r.Form中產生相應條目,如果我們用上面例子中的方式去獲取數據時程序就會報錯。所以我們需要通過`r.Form.Get()`來獲取值,因為如果字段不存在,通過該方式獲取的是空值。但是通過`r.Form.Get()`只能獲取單個的值,如果是map的值,必須通過上面的方式來獲取。
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/04.2.md#數字)數字
你想要確保一個表單輸入框中獲取的只能是數字,例如,你想通過表單獲取某個人的具體年齡是50歲還是10歲,而不是像“一把年紀了”或“年輕著呢”這種描述
如果我們是判斷正整數,那么我們先轉化成int類型,然后進行處理
~~~
getint,err:=strconv.Atoi(r.Form.Get("age"))
if err!=nil{
//數字轉化出錯了,那么可能就不是數字
}
//接下來就可以判斷這個數字的大小范圍了
if getint >100 {
//太大了
}
~~~
還有一種方式就是正則匹配的方式
~~~
if m, _ := regexp.MatchString("^[0-9]+$", r.Form.Get("age")); !m {
return false
}
~~~
對于性能要求很高的用戶來說,這是一個老生常談的問題了,他們認為應該盡量避免使用正則表達式,因為使用正則表達式的速度會比較慢。但是在目前機器性能那么強勁的情況下,對于這種簡單的正則表達式效率和類型轉換函數是沒有什么差別的。如果你對正則表達式很熟悉,而且你在其它語言中也在使用它,那么在Go里面使用正則表達式將是一個便利的方式。
> Go實現的正則是[RE2](http://code.google.com/p/re2/wiki/Syntax),所有的字符都是UTF-8編碼的。
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/04.2.md#中文)中文
有時候我們想通過表單元素獲取一個用戶的中文名字,但是又為了保證獲取的是正確的中文,我們需要進行驗證,而不是用戶隨便的一些輸入。對于中文我們目前有兩種方式來驗證,可以使用?`unicode`?包提供的?`func Is(rangeTab *RangeTable, r rune) bool`?來驗證,也可以使用正則方式來驗證,這里使用最簡單的正則方式,如下代碼所示
~~~
if m, _ := regexp.MatchString("^\\p{Han}+$", r.Form.Get("realname")); !m {
return false
}
~~~
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/04.2.md#英文)英文
我們期望通過表單元素獲取一個英文值,例如我們想知道一個用戶的英文名,應該是astaxie,而不是asta謝。
我們可以很簡單的通過正則驗證數據:
~~~
if m, _ := regexp.MatchString("^[a-zA-Z]+$", r.Form.Get("engname")); !m {
return false
}
~~~
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/04.2.md#電子郵件地址)電子郵件地址
你想知道用戶輸入的一個Email地址是否正確,通過如下這個方式可以驗證:
~~~
if m, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, r.Form.Get("email")); !m {
fmt.Println("no")
}else{
fmt.Println("yes")
}
~~~
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/04.2.md#手機號碼)手機號碼
你想要判斷用戶輸入的手機號碼是否正確,通過正則也可以驗證:
~~~
if m, _ := regexp.MatchString(`^(1[3|4|5|8][0-9]\d{4,8})$`, r.Form.Get("mobile")); !m {
return false
}
~~~
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/04.2.md#下拉菜單)下拉菜單
如果我們想要判斷表單里面``元素生成的下拉菜單中是否有被選中的項目。有些時候黑客可能會偽造這個下拉菜單不存在的值發送給你,那么如何判斷這個值是否是我們預設的值呢?
我們的select可能是這樣的一些元素
~~~
<select name="fruit">
<option value="apple">apple</option>
<option value="pear">pear</option>
<option value="banane">banane</option>
</select>
~~~
那么我們可以這樣來驗證
~~~
slice:=[]string{"apple","pear","banane"}
for _, v := range slice {
if v == r.Form.Get("fruit") {
return true
}
}
return false
~~~
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/04.2.md#單選按鈕)單選按鈕
如果我們想要判斷radio按鈕是否有一個被選中了,我們頁面的輸出可能就是一個男、女性別的選擇,但是也可能一個15歲大的無聊小孩,一手拿著http協議的書,另一只手通過telnet客戶端向你的程序在發送請求呢,你設定的性別男值是1,女是2,他給你發送一個3,你的程序會出現異常嗎?因此我們也需要像下拉菜單的判斷方式類似,判斷我們獲取的值是我們預設的值,而不是額外的值。
~~~
<input type="radio" name="gender" value="1">男
<input type="radio" name="gender" value="2">女
~~~
那我們也可以類似下拉菜單的做法一樣
~~~
slice:=[]int{1,2}
for _, v := range slice {
if v == r.Form.Get("gender") {
return true
}
}
return false
~~~
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/04.2.md#復選框)復選框
有一項選擇興趣的復選框,你想確定用戶選中的和你提供給用戶選擇的是同一個類型的數據。
~~~
<input type="checkbox" name="interest" value="football">足球
<input type="checkbox" name="interest" value="basketball">籃球
<input type="checkbox" name="interest" value="tennis">網球
~~~
對于復選框我們的驗證和單選有點不一樣,因為接收到的數據是一個slice
~~~
slice:=[]string{"football","basketball","tennis"}
a:=Slice_diff(r.Form["interest"],slice)
if a == nil{
return true
}
return false
~~~
上面這個函數`Slice_diff`包含在我開源的一個庫里面(操作slice和map的庫),[https://github.com/astaxie/beeku](https://github.com/astaxie/beeku)
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/04.2.md#日期和時間)日期和時間
你想確定用戶填寫的日期或時間是否有效。例如 ,用戶在日程表中安排8月份的第45天開會,或者提供未來的某個時間作為生日。
Go里面提供了一個time的處理包,我們可以把用戶的輸入年月日轉化成相應的時間,然后進行邏輯判斷
~~~
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
fmt.Printf("Go launched at %s\n", t.Local())
~~~
獲取time之后我們就可以進行很多時間函數的操作。具體的判斷就根據自己的需求調整。
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/04.2.md#身份證號碼)身份證號碼
如果我們想驗證表單輸入的是否是身份證,通過正則也可以方便的驗證,但是身份證有15位和18位,我們兩個都需要驗證
~~~
//驗證15位身份證,15位的是全部數字
if m, _ := regexp.MatchString(`^(\d{15})$`, r.Form.Get("usercard")); !m {
return false
}
//驗證18位身份證,18位前17位為數字,最后一位是校驗位,可能為數字或字符X。
if m, _ := regexp.MatchString(`^(\d{17})([0-9]|X)$`, r.Form.Get("usercard")); !m {
return false
}
~~~
上面列出了我們一些常用的服務器端的表單元素驗證,希望通過這個引導入門,能夠讓你對Go的數據驗證有所了解,特別是Go里面的正則處理。
- 第一章 Go環境配置
- 1.1 Go安裝
- 1.2 GOPATH 與工作空間
- 1.3 Go 命令
- 1.4 Go開發工具
- 1.5 小結
- 第二章 Go語言基礎
- 2.1 你好,Go
- 2.2 Go基礎
- 2.3 流程和函數
- 2.4 struct類型
- 2.5 面向對象
- 2.6 interface
- 2.7 并發
- 2.8 總結
- 第三章 Web基礎
- 3.1 Web工作方式
- 3.2 Go搭建一個Web服務器
- 3.3 Go如何使得Web工作
- 3.4 Go的http包詳解
- 3.5 小結
- 第四章 表單
- 4.1 處理表單的輸入
- 4.2 驗證表單的輸入
- 4.3 預防跨站腳本
- 4.4 防止多次遞交表單
- 4.5 處理文件上傳
- 4.6 小結
- 第五章 訪問數據庫
- 5.1 database/sql接口
- 5.2 使用MySQL數據庫
- 5.3 使用SQLite數據庫
- 5.4 使用PostgreSQL數據庫
- 5.5 使用beedb庫進行ORM開發
- 5.6 NOSQL數據庫操作
- 5.7 小結
- 第六章 session和數據存儲
- 6.1 session和cookie
- 6.2 Go如何使用session
- 6.3 session存儲
- 6.4 預防session劫持
- 6.5 小結
- 第七章 文本處理
- 7.1 XML處理
- 7.2 JSON處理
- 7.3 正則處理
- 7.4 模板處理
- 7.5 文件操作
- 7.6 字符串處理
- 7.7 小結
- 第八章 Web服務
- 8.1 Socket編程
- 8.2 WebSocket
- 8.3 REST
- 8.4 RPC
- 8.5 小結
- 第九章 安全與加密
- 9.1 預防CSRF攻擊
- 9.2 確保輸入過濾
- 9.3 避免XSS攻擊
- 9.4 避免SQL注入
- 9.5 存儲密碼
- 9.6 加密和解密數據
- 9.7 小結
- 第十章 國際化和本地化
- 10.1 設置默認地區
- 10.2 本地化資源
- 10.3 國際化站點
- 10.4 小結
- 第十一章 錯誤處理,調試和測試
- 11.1 錯誤處理
- 11.2 使用GDB調試
- 11.3 Go怎么寫測試用例
- 11.4 小結
- 第十二章 部署與維護
- 12.1 應用日志
- 12.2 網站錯誤處理
- 12.3 應用部署
- 12.4 備份和恢復
- 12.5 小結
- 第十三章 如何設計一個Web框架
- 13.1 項目規劃
- 13.2 自定義路由器設計
- 13.3 controller設計
- 13.4 日志和配置設計
- 13.5 實現博客的增刪改
- 13.6 小結
- 第十四章 擴展Web框架
- 14.1 靜態文件支持
- 14.2 Session支持
- 14.3 表單及驗證支持
- 14.4 用戶認證
- 14.5 多語言支持
- 14.6 pprof支持
- 14.7 小結
- 附錄A 參考資料