文章首發於 ISLAND
上一個章節中已經開始逐漸搭建了一個 web 頁面,現在我們開始逐步完善頁面上的功能,首先要完成的是登入和註冊功能。
接受表單資料
註冊頁面的 HTML 元素不在詳細寫出,具體頁面程式碼可以直接參考Github 上程式碼。
頁面完成後佈局:
註冊頁面有三個輸入框,分別為 email
,password
和 password again
。
完善後端 Gin 程式碼。我們在 initRouter
中 userGroup
中編寫新的介面。
userRouter.POST("/register", handler.UserRegister)
複製程式碼
編寫完新的介面就要開始編寫 Handler
。
func UserRegister(context *gin.Context) {
email := context.PostForm("email")
password := context.DefaultPostForm("password", "Wa123456")
passwordAgain := context.DefaultPostForm("password-again", "Wa123456")
println("email", email, "password", password, "password again", passwordAgain)
}
複製程式碼
UserRegister
方法中採用新的方式來接受 Post 請求提交的表單引數,PostForm
和 DefaultPostForm
。PostForm
直接接受引數,而 DefaultPostForm
可以設定一個預設值,如果前端沒有進行傳值,那麼我們可以設定預設值,如上面的程式碼,如果前端沒有將密碼傳輸過來我們可以設定一個預設密碼。
當我們執行並且輸入的時候,在控制檯上可以清楚的看到我們在表單上的輸入。
當我們專案功能完善的時候,就可以完善我們的單元測試。
此時的單元測試交之前有點複雜。
首先我們要構造一個結構,該結構是為了幫助我們將我們要提交的資訊存放到表單中,同時要指定請求頭資訊。
func TestUserPostForm(t *testing.T) {
value := url.Values{}
value.Add("email", "youngxhui@gmail.com")
value.Add("password", "1234")
value.Add("password-again", "1234")
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/user/register", bytes.NewBufferString(value.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded; param=value")
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
}
複製程式碼
單元測試編寫完成後可以執行單元測試,發現控制檯答應了我們在測試中寫的資料。
模型繫結
上例中我們的表單僅僅傳輸了三個引數,如果後期專案出現了十多個引數,每次寫一遍都很花費時間,也很消耗經歷。下面就對該方法進行改善
Gin 中提供了 模型繫結,將我們的表單資料與我們的模型進行一樣繫結。GIn會將資料統一封裝到模型中,方便我們日後使用。
首先定義我們的模型,新建 model
資料夾,建立 userModel.go
package model
type UserModel struct {
Email string `form:"email"`
Password string `form:"password"`
PasswordAgain string `form:"password-again"`
}
複製程式碼
通過 form:"email"
來對錶單中的 email
輸入資料進行繫結。然後需要修改一下 Handler
方法。
func UserRegister(context *gin.Context) {
var user model.UserModel
if err := context.ShouldBind(&user); err != nil {
println("err ->", err.Error())
return
}
println("email", user.Email, "password", user.Password, "password again", user.PasswordAgain)
}
複製程式碼
此時我們的模型繫結已經寫好,執行 TestUserPostForm
測試用例,測試用例可以完美的通過。說明我們的模型繫結方法是正確的。同時模型繫結還是從 json
, xml
,yml
等格式資料的繫結,日後會有介紹和說明。當然也可以通過瀏覽器中的登錄檔單進行提交。
資料校驗
做後端開發的人都明白一個道理:永遠不要相信前端傳過來的資料。所有的資料在進過後端時,務必要進行資料的校驗。
在模型中可用 binding
來對資料進行校驗。Gin 對於資料校驗使用的是 validator.v8
庫,該庫提供多種校驗方法。通過 binding:""
方式來進行對資料的校驗。
我們將 UserModel
進行修改,新增一些規則,郵箱驗證和密碼校驗,要求第二次重複密碼要和第一次密碼一致。更多的校驗規則可以看官方文件
type UserModel struct {
Email string `form:"email" binding:"email"`
Password string `form:"password"`
PasswordAgain string `form:"password-again" binding:"eqfield=Password"`
}
複製程式碼
我們重新寫一個測試用例用來測試郵箱和密碼校驗是否有效。
func TestUserPostFormEmailErrorAndPasswordError(t *testing.T) {
value := url.Values{}
value.Add("email", "youngxhui")
value.Add("password", "1234")
value.Add("password-again", "qwer")
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/user/register", bytes.NewBufferString(value.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded; param=value")
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
}
複製程式碼
執行測試,發現測試雖然通過了,但是會有兩行 error
資訊
err -> Key: 'UserModel.Email' Error:Field validation for 'Email' failed on the 'email' tag
Key: 'UserModel.PasswordAgain' Error:Field validation for 'PasswordAgain' failed on the 'eqfield' tag
複製程式碼
該資訊說明了我們的 Email
和 PasswordAgain
資訊校驗沒有通過。
使用Log和重定向
測試通過是因為無論我們程式碼如何都會返回 200 狀態碼,這是不符合http 狀態碼的規範的,所以我們要對http狀態碼進行規範化。同時我們之前的程式碼中一直使用 Printf
來列印日誌資訊,也是不規範的,因為 Printf
列印的日誌資訊相對侷限,所以應該選用 Log
進行日誌列印。
func UserRegister(context *gin.Context) {
var user model.UserModel
if err := context.ShouldBind(&user); err != nil {
log.Println("err ->", err.Error())
context.String(http.StatusBadRequest, "輸入的資料不合法")
} else {
log.Println("email", user.Email, "password", user.Password, "password again", user.PasswordAgain)
context.Redirect(http.StatusMovedPermanently, "/")
}
}
複製程式碼
首先我們將原來只用 Println
列印的資料都改成了 log
去列印資料。
同時將原來的狀態碼都進行了更改,不同的狀態碼代表不同的請求響應結果。
最後在請求成功的時候我們對路由進行了重定向,將頁面轉跳到首頁。
同時我們也要將測試用例裡的返回狀態碼進行修改。
總結
本節將表單提交,模型繫結和資料校驗有了一個相對細緻的介紹,程式碼中也通過不同的測試用例來檢查程式碼是否正確。
本章節程式碼
其他章節索引
Gin(一):Hello
Gin(二):路由Router
Gin(三):模板tmpl
Gin(四):表單提交校驗和模型繫結
Gin(五):連線MySQL
Gin(六):檔案的上傳
Gin(七):中介軟體的使用和定義
Gin(八):Cookie的使用
個人公眾號
最新文章都會在公眾號上進行分享,歡迎各位大佬關注