Golang中web參數(shù)校驗的實現(xiàn)
參數(shù)校驗
確保我們的應(yīng)用接收到的數(shù)據(jù)是格式正確并且安全的
基本用法
JSON
假設(shè)我們正在開發(fā)一個登錄接口,希望用戶必須提供用戶名和密碼。在 Gin 中,我們首先會定義一個 Go 的 struct 來描述這個數(shù)據(jù)結(jié)構(gòu)。
type Login struct {
User string `json:"user" binding:"required"`
Password string `json:"password" binding:"required"`
}
- json:“…” 標簽: 這個標簽告訴 Gin,當它解析傳入的 JSON 數(shù)據(jù)時,JSON 中的 user 字段應(yīng)該映射到我們結(jié)構(gòu)體中的 User 字段。
- binding:“required” 標簽: 這是最核心的部分!binding 就是用來做數(shù)據(jù)校驗的。required 是一個校驗規(guī)則,意思是“這個字段是必填的,不能為空”。
func loginHandler(c *gin.Context) {
var login Login
// 嘗試將請求的 JSON body 綁定到 login 結(jié)構(gòu)體上
// 在綁定的同時,Gin 會根據(jù) "binding" 標簽進行校驗
err := c.ShouldBindJSON(&login)
if err != nil {
// 如果 err 不是 nil,說明校驗失敗了!
// 比如,某個 "required" 的字段沒有被提供。
// Gin 通常會自動返回一個 400 Bad Request 錯誤。
c.JSON(400, gin.H{"error": err.Error()})
return
}
// 如果 err 是 nil,說明所有校驗都通過了!
c.JSON(200, gin.H{"status": "you are logged in"})
}

URL
除了處理 JSON,我們經(jīng)常還需要處理來自 URL 查詢(Query)和 HTML 表單(Form)的數(shù)據(jù)。
Gin 的設(shè)計非常統(tǒng)一。我們的 struct 定義方式幾乎不變,只需要更換一個綁定方法就可以了。
如何校驗 URL 查詢參數(shù),比如這樣一個請求:GET /search?keyword=gin&page=1
我們希望 keyword 是必填的,并且 page 必須是一個大于 0 的數(shù)字。
首先,還是定義我們的 struct:
type SearchQuery struct {
Keyword string `form:"keyword" binding:"required"`
Page int `form:"page" binding:"required,gt=0"`
}
- 我們用了 form:“…” 標簽,而不是 json:“…”。這個 form 標簽既可以用于 URL 查詢參數(shù),也可以用于表單數(shù)據(jù)。
- 我在 Page 字段的 binding 里加了一個新規(guī)則 gt=0,它的意思是 “greater than 0”(必須大于0)。
func searchHandler(c *gin.Context) {
var query SearchQuery
// 從 URL query 中綁定并校驗參數(shù)
// 注意這里換成了 ShouldBindQuery
err := c.ShouldBindQuery(&query)
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// 校驗通過!
c.JSON(200, gin.H{
"message": "search parameters are valid",
"keyword": query.Keyword,
"page": query.Page,
})
}

常用的校驗規(guī)則
我們通過一個用戶注冊的例子來看看:
type RegisterUser struct {
// 用戶名:必填,長度在 4 到 20 個字符之間
Username string `json:"username" binding:"required,min=4,max=20"`
// 郵箱:必填,且必須是合法的郵箱格式
Email string `json:"email" binding:"required,email"`
// 年齡:選填,但如果提供了,必須大于等于 18 歲,小于等于 100 歲
Age int `json:"age" binding:"gte=18,lte=100"`
// 用戶類型:必填,并且值必須是 'user' 或 'admin' 中的一個
UserType string `json:"user_type" binding:"required,oneof=user admin"`
}
- min=4,max=20:用于字符串、數(shù)組等,限制其最小和最大長度。多個規(guī)則用逗號 , 分隔。
- email:檢查字符串是否符合標準的 email 格式。
- gte=18:意思是 “Greater Than or Equal To”,即大于或等于 18。
- lte=100:意思是 “Less Than or Equal To”,即小于或等于 100。
- oneof=user admin:表示這個字段的值必須是后面列出的值之一(用空格分隔)。
func registerUserHandler(c *gin.Context) {
var register RegisterUser
err := c.ShouldBindJSON(®ister)
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{
"username": register.Username,
"email": register.Email,
})
}

自定義錯誤信息
核心思路是:捕獲到校驗錯誤后,我們不再直接把它整個返回,而是逐一檢查是哪個字段的哪條規(guī)則出錯了,然后根據(jù)這些信息,手動“翻譯”成我們想展示的話。
func registerUserHandler(c *gin.Context) {
var register RegisterUser
err := c.ShouldBindJSON(®ister)
if err != nil {
var verrs validator.ValidationErrors
ok := errors.As(err, &verrs)
if !ok {
// 如果不是 ValidationErrors 類型的錯誤,直接返回原始錯誤
c.JSON(400, gin.H{"error": err.Error()})
return
}
// 創(chuàng)建一個 map 來存放我們“翻譯”后的錯誤信息
// key 是字段名,value 是錯誤信息
// 比如:{"Username": "長度不能小于 4 個字符"}
translatedErrors := make(map[string]string)
for _, fe := range verrs {
// fe.Field() 獲取出錯的字段名,比如 "Username"
translatedErrors[fe.Field()] = getErrorMsg(fe)
}
c.JSON(400, gin.H{"errors": translatedErrors})
return
}
c.JSON(200, gin.H{
"username": register.Username,
"email": register.Email,
})
}
// 這個函數(shù)專門用來“翻譯”錯誤信息
func getErrorMsg(fe validator.FieldError) string {
// fe.Tag() 能獲取到是哪個校驗規(guī)則出錯了,比如 "required", "min", "email"
switch fe.Tag() {
case "required":
return "這是必填項"
case "min":
// fe.Param() 能獲取到規(guī)則后面的參數(shù),比如 "min=4" 中的 "4"
return "長度不能小于 " + fe.Param() + " 個字符"
case "max":
return "長度不能超過 " + fe.Param() + " 個字符"
case "email":
return "請輸入正確的郵箱地址"
case "oneof":
return "必須是 " + fe.Param() + " 中的一個"
default:
return "未知錯誤"
}
}

自定義校驗規(guī)則
當內(nèi)置的規(guī)則(像 required, email, min, max 等)不夠用時,我們就需要自定義規(guī)則。比如,我們想強制要求“密碼必須同時包含字母和數(shù)字”。
整個過程分為三步,我們一步一步來:
- 編寫一個自定義校驗函數(shù)
這個函數(shù)需要一個特定的格式。它接收一個 validator.FieldLevel 類型的參數(shù),并返回一個 bool 值。返回 true 表示校驗通過,false 表示失敗。
// isPasswordStrong 就是我們的自定義校驗函數(shù)
func isPasswordStrong(fl validator.FieldLevel) bool {
// fl.Field().String() 可以獲取到字段的字符串值
password := fl.Field().String()
hasLetter := false
hasDigit := false
// 遍歷密碼字符串,檢查是否同時包含字母和數(shù)字
for _, char := range password {
if unicode.IsLetter(char) {
hasLetter = true
}
if unicode.IsDigit(char) {
hasDigit = true
}
}
return hasLetter && hasDigit
}
將自定義函數(shù)“注冊”到 Gin 的校驗器里
光寫好函數(shù)還不行,我們得告訴 Gin 的校驗器:“嘿,我這里有一個新的校驗規(guī)則,它的名字叫 strong_password,對應(yīng)的處理函數(shù)是 isPasswordStrong。”這個注冊過程通常在你的程序啟動時(比如 main 函數(shù)里)完成。
// 從 Gin 的 binding 中獲取底層的 validator 引擎
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
// 注冊我們的自定義校驗函數(shù)
// 第一個參數(shù)是這個規(guī)則在 struct tag 中使用的名字
// 第二個參數(shù)是我們的函數(shù)
v.RegisterValidation("strong_password", isPasswordStrong)
}
- 在 Struct Tag 中使用新規(guī)則
一旦注冊成功,我們就可以像使用任何內(nèi)置規(guī)則一樣,在 struct tag 中使用 strong_password 了。

到此這篇關(guān)于Golang中web參數(shù)校驗的實現(xiàn)的文章就介紹到這了,更多相關(guān)Golang web參數(shù)校驗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言對JSON數(shù)據(jù)進行序列化和反序列化
這篇文章介紹了Go語言對JSON數(shù)據(jù)進行序列化和反序列化的方法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-07-07
詳解如何在golang項目開發(fā)中創(chuàng)建自己的Module
既然我們使用了很多開源的 module為我們的日常開發(fā)提供了很多的便捷性,那我們該如何實現(xiàn)自己的 module 來提供給團隊中使用,接下小編就給大家介紹一下在golang項目開發(fā)如何創(chuàng)建自己的Module,需要的朋友可以參考下2023-09-09
淺析Go項目中的依賴包管理與Go?Module常規(guī)操作
這篇文章主要為大家詳細介紹了Go項目中的依賴包管理與Go?Module常規(guī)操作,文中的示例代碼講解詳細,對我們深入了解Go語言有一定的幫助,需要的可以跟隨小編一起學(xué)習一下2023-10-10

