设为首页 加入收藏

TOP

golang 如何验证struct字段的数据格式(一)
2017-12-14 14:31:36 】 浏览:1319
Tags:golang 如何 验证 struct 段的 数据 格式

本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/125

假设我们有如下结构体:

type User struct {
    Id    int    
    Name  string 
    Bio   string 
    Email string 
}

我们需要对结构体内的字段进行验证合法性:

  • Id的值在某一个范围内。
  • Name的长度在某一个范围内。
  • Email格式正确。

我们可能会这么写:

user := User{
        Id:    0,
        Name:  "superlongstring",
        Bio:   "",
        Email: "foobar",
}

if user.Id < 1 && user.Id > 1000 {
    return false
}
if len(user.Name) < 2 && len(user.Name) > 10 {
    return false
}
if !validateEmail(user.Email) {
    return false
}

这样的话代码比较冗余,而且如果结构体新加字段,还需要再修改验证函数再加一段if判断。这样代码比较冗余。我们可以借助golang的structTag来解决上述的问题:

type User struct {
    Id    int    `validate:"number,min=1,max=1000"`
    Name  string `validate:"string,min=2,max=10"`
    Bio   string `validate:"string"`
    Email string `validate:"email"`
}

validate:"number,min=1,max=1000"就是structTag。如果对这个比较陌生的话,看看下面这个:


type User struct {
    Id        int       `json:"id"`
    Name      string    `json:"name"`
    Bio       string    `json:"about,omitempty"`
    Active    bool      `json:"active"`
    Admin     bool      `json:"-"`
    CreatedAt time.Time `json:"created_at"`
}

写过golang的基本都用过json:xxx这个用法,json:xxx其实也是一个structTag,只不过这是golang帮你实现好特定用法的structTag。而validate:"number,min=1,max=1000"是我们自定义的structTag。

实现思路

image

我们定义一个接口Validator,定义一个方法Validate。再定义有具体意义的验证器例如StringValidatorNumberValidatorEmailValidator来实现接口Validator
这里为什么要使用接口?假设我们不使用接口代码会怎么写?

if tagIsOfNumber(){
        validator := NumberValidator{}
}else if tagIsOfString() {
        validator := StringValidator{}
}else if tagIsOfEmail() {
        validator := EmailValidator{}
}else if tagIsOfDefault() {
        validator := DefaultValidator{}
}

这样的话判断逻辑不能写在一个函数中,因为返回值validator会因为structTag的不同而不同,而且validator也不能当做函数参数做传递。而我们定义一个接口,所有的validator都去实现这个接口,上述的问题就能解决,而且逻辑更加清晰和紧凑。
关于接口的使用可以看下标准库的io Writer,Writer是个interface,只有一个方法Writer:

type Writer interface {
    Write(p []byte) (n int, err error)
}

而输出函数可以直接调用参数的Write方法即可,无需关心到底是写到文件还是写到标准输出:

func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
    p := newPrinter()
    p.doPrintf(format, a)
    n, err = w.Write(p.buf)      //调用Write方法
    p.free()
    return
}

//调用
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 interface {
    Validate(interface{}) (bool, error)
}

type DefaultValidator struct {
}

func (v DefaultValidator) Validate(val interface{}) (bool, error) {
    return true, nil
}

type StringValidator struct {
    Min int
    Max int
}

func (v StringValidator) Validate(val interface{}) (bool, error) {
    l := len(val.(string))

    if l == 0 {
        return false, fmt.Errorf("cannot be blank")
    }

    if l < v.Min {
        return false, fmt.Errorf("should be at least %v chars long", v.Min)
    }

    if v.Max >= v.Min && l > v.Max {
        return false, fmt.Errorf("should be less than %v chars long", v.Max
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Golang学习--包管理工具glide 下一篇Beego 学习笔记15:布局页面

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目