ginvalidator 是一組 Gin 中間件,它包裝了我的另一個開源包 validatorgo 提供的廣泛的驗證器和消毒器集合。它還使用流行的開源套件 gjson 進行 JSON 欄位語法,提供從 JSON 物件中高效查詢和提取資料的功能。
它允許您以多種方式組合它們,以便您可以驗證和清理您的 Gin 請求,並提供工具來確定請求是否有效,以及根據您的驗證器匹配哪些資料。
它是基於流行的js/express庫express-validator
此版本的 ginvalidator 要求您的應用程式在 Go 1.16 上運行。
它也經過驗證可與 Gin 1.x.x.
為什麼不使用?
確保您的電腦上安裝了 Go。
go mod init example.com/learning
使用 go get 安裝必要的軟體包。
go get -u github.com/gin-gonic/gin
go get -u github.com/bube054/ginvalidator
學習東西的最好方法之一就是透過實例!因此,讓我們捲起袖子,開始編寫程式碼。
首先需要的是運行一個 Gin 伺服器。讓我們實作一個向某人打招呼的功能;為此,創建一個 main.go 然後添加以下程式碼:
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/hello", func(ctx *gin.Context) { person := ctx.Query("person") ctx.String(http.StatusOK, "Hello, %s!", person) }) r.Run() // listen and serve on 0.0.0.0:8080 }
現在透過在終端機上執行 go run main.go 來運行此檔案。
go mod init example.com/learning
HTTP 伺服器應該正在運行,您可以打開 http://localhost:8080/hello?person=John 向 John 致敬!
? 提示:
您可以將 Air 與 Go 和 Gin 結合使用來實現即時重新加載。每當文件更改時,這些都會自動重新啟動伺服器,因此您不必自己執行此操作!
所以伺服器正在工作,但有問題。最值得注意的是,當某人的名字未設定時,您不想向某人打招呼。
例如,造訪 http://localhost:8080/hello 將列印「Hello,」。
這就是 ginvalidator 派上用場的地方。它提供了用於驗證您的請求的驗證器、消毒器和修飾符。
讓我們新增一個驗證器和一個修飾符來檢查人員查詢字串不能為空,驗證器名為 Empty ,修飾符名為 Not:
go get -u github.com/gin-gonic/gin
? 注意:
為簡潔起見,程式碼範例中使用 gv 作為 ginvalidator 的別名。
現在,重新啟動伺服器,然後再次造訪 http://localhost:8080/hello。嗯,它仍然打印“Hello,!”...為什麼?
ginvalidator驗證鏈不會自動向使用者報告驗證錯誤。
原因很簡單:當您添加更多驗證器或更多欄位時,您希望如何收集錯誤?您想要一份所有錯誤的列表,每個字段只有一個,整體只有一個......?
所以下一個明顯的步驟是再次更改上面的程式碼,這次使用 ValidationResult 函數驗證驗證結果:
go get -u github.com/bube054/ginvalidator
現在,如果您再次訪問 http://localhost:8080/hello,您將看到以下 JSON 內容,為了清晰起見,我們進行了格式化:
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/hello", func(ctx *gin.Context) { person := ctx.Query("person") ctx.String(http.StatusOK, "Hello, %s!", person) }) r.Run() // listen and serve on 0.0.0.0:8080 }
現在,這告訴我們的是
這是一個更好的場景,但仍然可以改進。我們繼續吧。
所有請求位置驗證器都接受可選的第二個參數,它是用來格式化錯誤訊息的函數。如果提供 nil,將使用預設的通用錯誤訊息,如上面的範例所示。
go run main.go
現在,如果您再次造訪 http://localhost:8080/hello,您將看到以下 JSON 內容,並帶有新的錯誤訊息:
package main import ( "net/http" gv "github.com/bube054/ginvalidator" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/hello", gv.NewQuery("person", nil). Chain(). Not(). Empty(nil). Validate(), func(ctx *gin.Context) { person := ctx.Query("person") ctx.String(http.StatusOK, "Hello, %s!", person) }) r.Run() }
您可以使用 GetMatchedData,它會自動收集 ginvalidator 已驗證和/或清理的所有資料。然後可以使用 MatchedData 的 Get 方法存取此資料:
go mod init example.com/learning
開 http://localhost:8080/hello?person=John 向 John 致敬!
可用位置有 BodyLocation、CookieLocation、QueryLocation、ParamLocation 和 HeaderLocation。
每個位置都包含一個 String 方法,該方法傳回儲存經過驗證/清理的資料的位置。
雖然用戶不能再發送空人名,但它仍然可以將 HTML 注入您的頁面!這稱為跨站腳本漏洞 (XSS)。
讓我們看看它是如何工作的。前往 http://localhost:8080/hello?person=John,您應該會看到「Hello, John!」。
雖然此範例很好,但攻擊者可以將人員查詢字串變更為 <script> 。載入自己的 JavaScript 的標籤可能有害。 <br>
在這種情況下,緩解 ginvalidator 問題的一種方法是使用消毒劑,特別是 Escape,它將特殊的 HTML 字元與其他可以表示為文字的字元進行轉換。 <br>
</script>
go get -u github.com/gin-gonic/gin
現在,如果您重新啟動伺服器並重新整理頁面,您將看到「Hello, John!」。我們的範例頁面不再容易受到 XSS 攻擊!
⚠️ 註:
ginvalidator 在清理期間不會修改 http.Request 值。若要存取清理後的數據,請務必使用 GetMatchedData 函數。
驗證鍊是 ginvalidator 中的主要概念之一,因此了解它很有用,以便您可以有效地使用它。
但不用擔心:如果您已經閱讀了入門指南,那麼您已經在不知不覺中使用了驗證鏈!
驗證鍊是使用以下函數建立的,每個函數都針對 HTTP 請求中的特定位置:
它們之所以有這個名字,是因為它們用驗證(或清理)來包裝字段的值,並且它的每個方法都返回自身。
這種模式通常稱為方法鏈,因此稱為驗證鏈。
驗證鏈不僅具有許多用於定義驗證、清理和修改的有用方法,而且還具有傳回 Gin 中介軟體處理函數的 Validate 方法。
這是驗證鏈通常如何使用以及如何閱讀的範例:
go mod init example.com/learning
驗證鏈有三種方法:驗證器、消毒器和修飾器。
驗證器決定請求欄位的值是否有效。這表示檢查該欄位是否採用您期望的格式。例如,如果您正在建立註冊表單,您的要求可能是使用者名稱必須是電子郵件地址,並且密碼的長度必須至少為 8 個字元。
如果該值無效,則會使用一些錯誤訊息來記錄該欄位的錯誤。然後可以稍後在 Gin 路由處理程序中檢索此驗證錯誤並將其傳回給使用者。
他們是:
消毒劑會改變場值。它們對於消除值中的噪音很有用,甚至可能提供一些針對威脅的基本防線。
Sanitizers 將更新後的欄位值保留回 Gin 上下文中,以便其他 ginvalidator 函數、您自己的路由處理程序程式碼,甚至其他中間件都可以使用它。
他們是:
修飾符定義驗證鏈運行時的行為方式。
他們是:
? 注意:
這些方法在 pkg.go.dev ginvalidator 文件中使用 GoDoc 進行了完整記錄。如果有任何細節不清楚,您可能還需要參考 validatorgo 套件中的相關函數以獲取更多上下文,我將在下面進行解釋。
驗證鏈公開的所有功能實際上都來自 validatorgo,這是我的其他開源 go 包之一,專門從事字串驗證/清理。請檢查一下,加星號並分享? ? ? ,謝謝。
這包括所有 validatorgo 驗證器和清理程序,從常用的 IsEmail、IsLength 和 Trim 到更小眾的 IsISBN、IsMultibyte 和 StripLow!
這些在 ginvalidator 被稱為標準驗證器和標準消毒器。但沒有來自 validatorgo 的 Is 前綴。
在驗證鏈上呼叫方法的順序通常很重要。
它們幾乎總是按照指定的順序運行,因此您只需閱讀驗證鏈的定義(從第一個連結方法到最後一個方法)就可以知道驗證鏈將做什麼。
以下面的程式碼片段為例:
go mod init example.com/learning
在這種情況下,如果使用者傳遞一個僅由空格組成的「search_query」值,它不會為空,因此驗證通過。但由於 .Trim() 消毒劑存在,空格將被刪除,並且該字段將變為空,因此您實際上最終會得到誤報。
現在,將其與以下程式碼片段進行比較:
go get -u github.com/gin-gonic/gin
這條鏈將更明智地刪除空格,然後驗證值是否不為空。
此規則的一個例外是 .Optional():它可以放置在鏈中的任何一點,並將該鏈標記為可選。
如果您希望重複使用同一個鏈,最好從函數中傳回它們:
go get -u github.com/bube054/ginvalidator
在 ginvalidator 中,欄位是任何經過驗證或清理的值,並且它是字串。
幾乎每個函數或值都以某種方式由 ginvalidator 引用欄位傳回。因此,了解欄位路徑語法對於選擇驗證欄位以及存取驗證錯誤或驗證資料時非常重要。
正文欄位僅對以下內容類型有效:
go mod init example.com/learning
使用路徑 user.name,擷取的值為「John」。
go get -u github.com/gin-gonic/gin
身體:
go get -u github.com/bube054/ginvalidator
欄位“name”的值為“John”,“email”的值為“john.doe@example.com”。
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/hello", func(ctx *gin.Context) { person := ctx.Query("person") ctx.String(http.StatusOK, "Hello, %s!", person) }) r.Run() // listen and serve on 0.0.0.0:8080 }
身體:
go run main.go
欄位“name”的值為“John”,“file”為上傳的檔案。
查詢欄位對應URL搜尋參數,其值自動為Gin未轉義的url。
範例:
package main import ( "net/http" gv "github.com/bube054/ginvalidator" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/hello", gv.NewQuery("person", nil). Chain(). Not(). Empty(nil). Validate(), func(ctx *gin.Context) { person := ctx.Query("person") ctx.String(http.StatusOK, "Hello, %s!", person) }) r.Run() }
package main import ( "net/http" gv "github.com/bube054/ginvalidator" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/hello", gv.NewQuery("person", nil). Chain(). Not(). Empty(nil). Validate(), func(ctx *gin.Context) { result, err := gv.ValidationResult(ctx) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ "message": "The server encountered an unexpected error.", }) return } if len(result) != 0 { ctx.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{ "errors": result, }) return } person := ctx.Query("person") ctx.String(http.StatusOK, "Hello, %s!", person) }) r.Run() }
Param 欄位 表示 URL 路徑參數,其值會被 ginvalidator 自動轉義。
範例:
{ "errors": [ { "location": "queries", "message": "Invalid value", "field": "person", "value": "" } ] }
標頭欄位是HTTP請求標頭,它們的值不是未轉義的。如果您提供非規範標頭金鑰,將會出現日誌警告。
範例:
package main import ( "net/http" gv "github.com/bube054/ginvalidator" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/hello", gv.NewQuery("person", func(initialValue, sanitizedValue, validatorName string) string { return "Please enter your name." }, ).Chain(). Not(). Empty(nil). Validate(), func(ctx *gin.Context) { result, err := gv.ValidationResult(ctx) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ "message": "The server encountered an unexpected error.", }) return } if len(result) != 0 { ctx.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{ "errors": result, }) return } person := ctx.Query("person") ctx.String(http.StatusOK, "Hello, %s!", person) }) r.Run() }
Cookie 欄位 是 HTTP cookie,其值自動為 Gin 未轉義的 url。
範例:
{ "errors": [ { "location": "queries", "message": "Please enter your name.", "field": "person", "value": "" } ] }
如果您正在建置的伺服器不是一個非常簡單的伺服器,那麼遲早您將需要驗證器、清理器和錯誤訊息,而不僅僅是 ginvalidator 內建的內容。
ginvalidator 無法滿足您並且您可能會遇到的一個經典需求是在使用者註冊時驗證電子郵件地址是否正在使用。
可以透過實作自訂驗證器在 ginvalidator 中執行此操作。
CustomValidator 是驗證鏈上可用的方法,它接收特殊函數 CustomValidatorFunc,並且必須傳回一個布林值來確定欄位是否有效。
CustomSanitizer 也是驗證鏈上可用的方法,它接收特殊函數 CustomSanitizerFunc,並且必須傳回新的清理值。
CustomValidator 可以透過使用 goroutine 和sync.WaitGroup 來非同步處理並發操作。在驗證器中,您可以為每個非同步任務啟動 goroutine,並將每個任務新增至 WaitGroup。所有任務完成後,驗證器應傳回一個布林值。
例如,為了檢查電子郵件是否未被使用:
go mod init example.com/learning
或您也可以驗證密碼是否與重複符合:
go get -u github.com/gin-gonic/gin
⚠️ 註:
如果請求正文將被多次存取(無論是在同一驗證鏈中、在同一請求上下文的另一個驗證鏈中還是在後續處理程序中),請確保在每次讀取後重置請求正文。如果不這樣做,再次讀取正文時可能會導致錯誤或資料遺失。
CustomSanitizer 沒有太多規則。無論它們傳回什麼值,都是該欄位將取得的新值。
自訂清理程序也可以透過使用 goroutine 和sync.WaitGroup 來非同步處理並發操作。
go get -u github.com/bube054/ginvalidator
每當欄位值無效時,都會為其記錄錯誤訊息。
預設錯誤訊息是“無效值”,它根本無法描述錯誤的內容,因此您可能需要對其進行自訂。您可以透過
進行客製化
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/hello", func(ctx *gin.Context) { person := ctx.Query("person") ctx.String(http.StatusOK, "Hello, %s!", person) }) r.Run() // listen and serve on 0.0.0.0:8080 }
有關驗證器名稱的完整列表,請參閱 ginvalidator 常數。
以上是使用 ginvalidator 簡化 Go 中的 Gin 輸入驗證的詳細內容。更多資訊請關注PHP中文網其他相關文章!