java 自定义validate_Golang-03 自定义validator,实现java注解功能-Go语言中文社区
0x00 About
接口開(kāi)發(fā)中, 比較常用的操作就是對(duì)輸入的參數(shù)Bean進(jìn)行字段屬性值校驗(yàn).
在Java中, 有Annotation(注解)可以讓我們方便的在的類上面添加校驗(yàn)信息,
那么在Go中應(yīng)該如何做到這一點(diǎn)呢?
0x01 structTag
StructType = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName .
Tag = string_lit .
也就是說(shuō), struct字段的后面, 可以添加一個(gè)字符串, 稱之為T(mén)ag
然后, 通過(guò)go語(yǔ)言中的reflect反射機(jī)制, 就可以讀取相應(yīng)字段的Tag信息了.
接下來(lái), 找一段代碼直觀感受一下:
type InParam struct {
StudentName string `json:"name" cc:"str,min=5,max=15"`
Score int `json:"score" cc:"num,min=0,max=100"`
}
package main
import (
"fmt"
"reflect"
)
func main() {
t := reflect.TypeOf(&InParam{"18", 25})
field := t.Elem().Field(0)
jsonName := field.Tag.Get("json")
cc := field.Tag.Get("cc")
fmt.Printf("%s(%s): %s", field.Name, jsonName, cc)
}
輸出結(jié)果:
StudentName(name): str,min=5,max=15
需要注意的地方: reflect.TypeOf 的參數(shù)只能接入對(duì)象.
0x02 Validator
那么接下來(lái)的工作, 就是根據(jù)Tag內(nèi)容開(kāi)發(fā)一個(gè)公共函數(shù).
這里提供了兩種驗(yàn)證器:string和number, 分別驗(yàn)證其長(zhǎng)度范圍和取值范圍.
package main
import (
"fmt"
"reflect"
"strings"
)
// 定義接口
type CCValid interface {
Validate(interface{}) (bool, error)
}
// 定義三個(gè)驗(yàn)證器
type CCDefaultValid struct {
}
type CCNumberValid struct {
Min int
Max int
}
type CCStringValid struct {
Min int
Max int
}
// 三個(gè)驗(yàn)證器實(shí)現(xiàn)接口方法
func (c CCNumberValid) Validate(obj interface{}) (bool, error) {
v := obj.(int)
if v < c.Min || v > c.Max {
return false, fmt.Errorf(":int value should in range (%d, %d)", c.Min, c.Max)
}
return true, nil
}
func (c CCStringValid) Validate(obj interface{}) (bool, error) {
l := len(obj.(string))
if l < c.Min || l > c.Max {
return false, fmt.Errorf(":string length should in range (%d, %d)", c.Min, c.Max)
}
return true, nil
}
func (c CCDefaultValid) Validate(obj interface{}) (bool, error) {
return true, nil
}
var tagName = "cc"
// 公共方法,對(duì)外提供檢驗(yàn)處理
func CCValidate(s interface{}) []error {
var errs []error
v := reflect.ValueOf(s)
for i := 0; i < v.NumField(); i++ {
tag := v.Type().Field(i).Tag.Get(tagName)
if tag == "" || tag == "-" {
continue
}
validator := parseValidatorFromTag(tag)
valid, err := validator.Validate(v.Field(i).Interface())
if !valid && err != nil {
errs = append(errs, fmt.Errorf("%s%s", v.Type().Field(i).Name, err.Error()))
}
}
return errs
}
// 從Tag字符串里分析出使用哪個(gè)驗(yàn)證器,并賦值
func parseValidatorFromTag(tag string) CCValid {
args := strings.Split(tag, ",")
switch args[0] {
case "num":
v := CCNumberValid{}
fmt.Sscanf(strings.Join(args[1:], ","), "min=%d,max=%d", &v.Min, &v.Max)
return v
case "str":
v := CCStringValid{}
fmt.Sscanf(strings.Join(args[1:], ","), "min=%d,max=%d", &v.Min, &v.Max)
return v
}
return CCDefaultValid{}
}
0x03 在gin中的使用
我們開(kāi)發(fā)校驗(yàn)器的目的, 當(dāng)然, 是為了能在gin中使用啦
直接上代碼, 理解起來(lái)不難:
type Result struct {
code int
msg string
data interface{}
}
func OK(msg string) Result {
return Result{0, msg, nil}
}
func NG(msg string) Result {
return Result{1, msg, nil}
}
// 解析JSON請(qǐng)求
r.POST("/login", func(c *gin.Context) {
var header MyHeader
var param Login
// 解析Header
if c.BindHeader(&header) != nil {
c.JSON(200, NG("invalid header"))
return
}
// 解析JSON
if c.BindJSON(¶m) != nil {
c.JSON(200, NG("invalid json param"))
return
}
// 驗(yàn)證
for _, firstErr := range CCValidate(param) {
c.JSON(200, NG(firstErr.Error()))
return
}
c.JSON(200, gin.H{"hello": param.Username, "world": param.Password, "from": header.From})
})
當(dāng)驗(yàn)證出錯(cuò)時(shí), 會(huì)向客戶端返回第一個(gè)發(fā)現(xiàn)的錯(cuò)誤.
0x04 TODO
現(xiàn)在的代碼來(lái)看, 業(yè)務(wù)功能還沒(méi)開(kāi)發(fā)呢, 已經(jīng)寫(xiě)了這么多行代碼了. 一點(diǎn)都不優(yōu)雅.
接下來(lái)要考慮, 能不能利用中間件, 完成這些前置操作.
總結(jié)
以上是生活随笔為你收集整理的java 自定义validate_Golang-03 自定义validator,实现java注解功能-Go语言中文社区的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java getresourceasst
- 下一篇: 抢椅子游戏java_游戏教案小班抢椅子