golang 如何驗證struct欄位的資料格式

2021-09-24 15:58:29 字數 4140 閱讀 4714

本文同時發表在

假設我們有如下結構體:

type user struct
我們需要對結構體內的字段進行驗證合法性:

我們可能會這麼寫:

user := user

if user.id < 1 && user.id > 1000

if len(user.name) < 2 && len(user.name) > 10

if !validateemail(user.email)

這樣的話**比較冗餘,而且如果結構體新加字段,還需要再修改驗證函式再加一段if判斷。這樣**比較冗餘。我們可以借助golang的structtag來解決上述的問題:

type user struct
validate:"number,min=1,max=1000"就是structtag。如果對這個比較陌生的話,看看下面這個:

type user struct
寫過golang的基本都用過json:***這個用法,json:***其實也是乙個structtag,只不過這是golang幫你實現好特定用法的structtag。而validate:"number,min=1,max=1000"是我們自定義的structtag。

實現思路

我們定義乙個介面validator,定義乙個方法validate。再定義有具體意義的驗證器例如strin**alidatornumbervalidatoremailvalidator來實現介面validator

這裡為什麼要使用介面?假設我們不使用介面**會怎麼寫?

if tagisofnumber()

}else if tagisofstring()

}else if tagisofemail()

}else if tagisofdefault()

}

這樣的話判斷邏輯不能寫在乙個函式中,因為返回值validator會因為structtag的不同而不同,而且validator也不能當做函式引數做傳遞。而我們定義乙個介面,所有的validator都去實現這個介面,上述的問題就能解決,而且邏輯更加清晰和緊湊。

關於介面的使用可以看下標準庫的io writer,writer是個inte***ce,只有乙個方法writer:

type writer inte***ce
而輸出函式可以直接呼叫引數的write方法即可,無需關心到底是寫到檔案還是寫到標準輸出:

func fprintf(w io.writer, format string, a ...inte***ce{}) (n int, err error) 

//呼叫

fprintf(os.stdout, format, a...) //標準輸出

fprintf(os.stderr, msg+"\n", args...) //標準錯誤輸出

var buf bytes.buffer

fprintf(&buf, "[") //寫入到buffer的快取中

言歸正傳,我們看下完整**,**是custom struct field tags in golang中給出的:

package main

import (

"fmt"

"reflect"

"regexp"

"strings"

)const tagname = "validate"

//郵箱驗證正則

var mailre = regexp.mustcompile(`\a[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z`)

//驗證介面

type validator inte***ce ) (bool, error)

}type defaultvalidator struct

func (v defaultvalidator) validate(val inte***ce{}) (bool, error)

type strin**alidator struct

func (v strin**alidator) validate(val inte***ce{}) (bool, error)

if l < v.min

if v.max >= v.min && l > v.max

return true, nil

}type numbervalidator struct

func (v numbervalidator) validate(val inte***ce{}) (bool, error)

if v.max >= v.min && num > v.max

return true, nil

}type emailvalidator struct

func (v emailvalidator) validate(val inte***ce{}) (bool, error)

return true, nil

}func getvalidatorfromtag(tag string) validator

//將structtag中的min和max解析到結構體中

fmt.sscanf(strings.join(args[1:], ","), "min=%d,max=%d", &validator.min, &validator.max)

return validator

case "string":

validator := strin**alidator{}

fmt.sscanf(strings.join(args[1:], ","), "min=%d,max=%d", &validator.min, &validator.max)

return validator

case "email":

return emailvalidator{}

}return defaultvalidator{}

}func validatestruct(s inte***ce{}) error

v := reflect.valueof(s)

for i := 0; i < v.numfield(); i++

validator := getvalidatorfromtag(tag)

valid, err := validator.validate(v.field(i).inte***ce())

if !valid && err != nil

}return errs

}type user struct

func main()

fmt.println("errors:")

for i, err := range validatestruct(user)

}

**很好理解,結構也很清晰,不做過多解釋了^_^

github上其實已經有現成的驗證包了govalidator,支援內建支援的驗證tag和自定義驗證tag:

//自定義tag驗證函式

govalidator.tagmap["machine_id"] = govalidator.validator(func(str string) bool )

if ok, err := govalidator.validatestruct(server); err != nil else

}

Golang 資料驗證validator

在web應用中會碰到各種欄位的校驗 比如使用者名稱 密碼 郵箱等 如果按流程順序校驗 會很長而且很難看 這裡可以使用validator包 來幫助對字段的校驗 記錄一下 備忘 package main import fmt validator gopkg.in go playground valida...

golang 驗證密碼(自定義驗證項)

const 密碼驗證選項 只能含有 pwd opt number uint16 1 iota 數字 0001 pwd opt lower 小寫 0010 pwd opt upper 大寫 0100 pwd opt special 特殊符號 1000 func verifypwd pwd string...

golang 請求帶驗證資訊的坑

最近用golang 和python對接介面,由於之前驗證那塊沒有設定好,然後又為了進度,最近決定用http自帶的basic 驗證,php的 很快就驗證通過了 param url param filename param path param type 上傳 private function uplo...