本文同時發表在
假設我們有如下結構體:
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**alidator
、numbervalidator
、emailvalidator
來實現介面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...