本文為轉載,原文:Golang Web學習(14)—— 表單處理
介紹
表單是我們平常編寫Web應用常用的工具,通過表單我們可以方便的讓客戶端和服務器進 行數據的交互。對于以前開發過Web的用戶來說表單都非常熟悉。
表單是一個包含表單元素的區域。表單元素是允許用戶在表單中(比如:文本域、下拉列表、 單選框、復選框等等)輸入信息的元素。表單使用表單標簽(<form>
)定義。
1、處理表單的輸入
先看一個例子吧。
在之前文章的代碼基礎之下,創建一個view文件夾,用來存放界面的代碼文件,在view文件中創建一個login.ctpl文件,用來寫前端代碼,后綴名可根據自己喜好自定義。下面看下這個文件內的代碼:
<html>
<head>
<title>登錄</title>
</head>
<body>
<form action="http://localhost:9090/login" method="post">
用戶名:<input type="text" name="username">
密碼:<input type="password" name="password">
<input type="submit" value="登陸">
</form>
</body>
</html>
從代碼中可知,我們的界面非常簡單,就是個簡單的用戶名,密碼的表單,且通過Post方法提交到http://localhost:9090/login
。
然后我們再看一下這個后端對于login的處理,同樣在之前的代碼基礎上,在myserver.go
文件中添加一個login的函數。
代碼如下:
func login(w http.ResponseWriter, r *http.Request){
r.ParseForm() //解析form
fmt.Println("method: ", r.Method)
//判斷傳遞方式
if r.Method == "GET"{
//加載界面模板
t, _ := template.ParseFiles("./view/login.ctpl")
//將解析好的模板應用到data上,這里data為nil
t.Execute(w, nil)
}else if r.Method == "POST"{
fmt.Println("username: ", r.Form["username"])
fmt.Println("password: ", r.Form["password"])
}
}
當代碼執行到login函數的時候,首先我們要判斷是通過什么方式傳遞過來的,是GET,還是POST。這個我們可以在r.Method
中取得,然后根據不同的方法分別處理。
在get方法中,我們調用了html/template
包中的2個函數,下面看下這兩個函數的定義吧:
- ParseFiles
func (t *Template) ParseFiles(filenames ...string) (*Template, error)
ParseFiles方法解析filenames指定的文件里的模板定義并將解析結果與t關聯。如果發生錯誤,會停止解析并返回nil,否則返回(t, nil)。至少要提供一個文件。
- Execute
func (t *Template) Execute(wr io.Writer, data interface{}) error
Execute方法將解析好的模板應用到data上,并將輸出寫入wr。如果執行時出現錯誤,會停止執行,但有可能已經寫入wr部分數據。模板可以安全的并發執行。
在Post方法中,這里只是做了簡單的控制臺打印,檢測是否跟我們輸入的一致。
好,我們的login函數寫好了,那么我們怎么調用呢?當然是路由嘍,在之前的自定義的路由中加入一個新的路由,補充myserver.go
中的ServeHTTP
代碼如下:
func (p *MyMux)ServeHTTP(w http.ResponseWriter, r *http.Request){
if r.URL.Path == "/"{
sayHelloName(w, r)
return
}
if r.URL.Path == "/about"{
about(w, r)
return
}
if r.URL.Path == "/login"{
login(w,r)
return
}
http.NotFound(w,r)
return
}
然后運行,跑起來。瀏覽器地址欄中輸入:http://localhost:9090/login
,結果如下:
在頁面中輸入:用戶名:chain 密碼:123456 并點擊登錄按鈕。控制臺結果如下:
如圖所示,我們打印出來了我們的輸入。
request.Form是一個url.Values類型,里面存儲的是對應的類似key=value的信息,下面 展示了可以對form數據進行的一些操作:
v := url.Values{}
v.Set("name", "Ava")
v.Add("friend", "Jess")
v.Add("friend", "Sarah") v.Add("friend", "Zoe")
// v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe"
fmt.Println(v.Get("name"))
fmt.Println(v.Get("friend"))
fmt.Println(v["friend"])
Tips: Request本身也提供了FormValue()函數來獲取用戶提交的參數。如 r.Form["username"]也可寫成r.FormValue("username")。調用r.FormValue時會自動調用 r.ParseForm,所以不必提前調用。r.FormValue只會返回同名參數中的第一個,若參數不存 在則返回空字符串。
2、驗證表單的輸入
開發Web的一個原則就是,不能信任用戶輸入的任何信息,所以驗證和過濾用戶的輸入信 息就變得非常重要,我們經常會在微博、新聞中聽到某某網站被入侵了,存在什么漏洞,這 些大多是是因為網站對于用戶輸入的信息沒有做嚴格的驗證引起的,所以為了編寫出安全 可靠的Web程序,驗證表單輸入的意義重大。
我們平常編寫Web應用主要有兩方面的數據驗證,一個是在頁面端的js驗證(目前在這方 面有很多的插件庫,比如ValidationJS插件),一個是在服務器端的驗證,我們這小節講解 的是如何在服務器端驗證。
必填字段
通過len函數獲取表單字段長度來判斷字段是否為空:
if len(r.Form["username"][0])==0{
//為空的處理
}
r.Form對不同類型的表單元素的留空有不同的處理, 對于空文本框、空文本區域以及文件 上傳,元素的值為空值,而如果是未選中的復選框和單選按鈕,則根本不會在r.Form中產生 相應條目,如果我們用上面例子中的方式去獲取數據時程序就會報錯。所以我們需要通過 r.Form.Get()來獲取值,因為如果字段不存在,通過該方式獲取的是空值。但是通過 r.Form.Get()只能獲取單個的值,如果是map的值,必須通過上面的方式來獲取。
數字
如果我們是判斷正整數,那么我們先轉化成int類型,然后進行處理
age, err := strconv.Atoi(r.Form.Get("age"))
if err != nil{
fmt.Fprintf(w, "The format of the input is not correct")
}
if age < 18{
fmt.Fprintf(w, "Minors are not registered")
}
下拉框,復選框,單選框
這種的校驗可以預先在服務端創建與客戶端選項一致的切片,當客戶端提交的時候再與服務端的相比較,決策出輸入是否合理。
正則表達式
對于一些復雜的字符串可能需要用正則表達式來匹配
if m, _ := regexp.MatchString(`表達式字符串`,
r.Form.Get("key")); !m {
//錯誤處理
}
下面提供幾種正則匹配規則:
- 中文匹配
^[\\x{4e00}-\\x{9fa5}]+$
- 英文
^[a-zA-Z]+$
- 郵箱
`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`
- 手機號碼
`^(1[3|4|5|8][0-9]\d{4,8})$`
- 身份證號碼
//驗證15位身份證,15位的是全部數字
`^(\d{15})$`
//驗證18位身份證,18位前17位為數字,最后一位是校驗位,可能為數字或字符X。
`^(\d{17})([0-9]|X)$`
例子
修改下之前的login.ctpl和login函數的代碼,測試下我們的表單驗證。
login.ctpl:
<html>
<head>
<title></title>
</head>
<body>
<form action="http://localhost:9090/login" method="post">
用戶名:<input type="text" name="username">
年齡:<input type="number" name="age">
郵箱:<input type="text" name="email">
<input type="submit" value="按鈕">
</form>
</body>
</html>
login函數
func login(w http.ResponseWriter, r *http.Request){
r.ParseForm() //解析form
fmt.Println("method: ", r.Method)
if r.Method == "GET"{
t, _ := template.ParseFiles("./view/login.ctpl")
t.Execute(w, nil)
}else if r.Method == "POST"{
if len(r.Form["username"][0])==0{
fmt.Fprintf(w, "username: null or empty \n")
}
age, err := strconv.Atoi(r.Form.Get("age"))
if err != nil{
fmt.Fprintf(w, "age: The format of the input is not correct \n")
}
if age < 18{
fmt.Fprintf(w, "age: Minors are not registered \n")
}
if m, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`,
r.Form.Get("email")); !m {
fmt.Fprintf(w, "email: The format of the input is not correct \n")
}
}
}
運行結果:
源碼
完
轉載請注明出處:
Golang Web學習(14)—— 表單處理
目錄
上一節:Golang Web學習(13)—— 搭建簡單的Web服務器
下一節:Golang Web學習(15)—— 文件上傳