go语言mysql遍历字段名_Go语言获取结构体字段标识

在本节,我们将看到如何通过反射机制类获取成员标签。

对于一个 web 服务,大部分 HTTP 处理函数要做的第一件事情就是展开请求中的参数到本地变量中。我们定义了一个工具函数,叫 params.Unpack,通过使用结构体成员标签机制来让 HTTP 处理函数解析请求参数更方便。

首先,我们看看如何使用它。下面的 search 函数是一个 HTTP 请求处理函数。它定义了一个匿名结构体类型的变量,用结构体的每个成员表示 HTTP 请求的参数。

其中结构体成员标签指明了对于请求参数的名字,为了减少 URL 的长度这些参数名通常都是神秘的缩略词。Unpack 将请求参数填充到合适的结构体成员中,这样我们可以方便地通过合适的类型类来访问这些参数。

import "gopl.io/ch12/params"

// 搜索实现/search url端点。

func search(resp http.ResponseWriter, req *http.Request) {

var data struct {

Labels []string `http:"l"`

MaxResults int `http:"max"`

Exact bool `http:"x"`

}

data.MaxResults = 10 // set default

if err := params.Unpack(req, &data); err != nil {

http.Error(resp, err.Error(), http.StatusBadRequest) // 400

return

}

// ...处理程序的其余部分...

fmt.Fprintf(resp, "Search: %+v\n", data)

}

下面的 Unpack 函数主要完成三件事情。第一,它调用 req.ParseForm() 来解析 HTTP 请求。然后,req.Form 将包含所有的请求参数,不管 HTTP 客户端使用的是 GET 还是 POST 请求方法。

下一步,Unpack 函数将构建每个结构体成员有效参数名字到成员变量的映射。如果结构体成员有成员标签的话,有效参数名字可能和实际的成员名字不相同。reflect.Type 的 Field 方法将返回一个 reflect.StructField,里面含有每个成员的名字、类型和可选的成员标签等信息。

其中成员标签信息对应 reflect.StructTag 类型的字符串,并且提供了 Get 方法用于解析和根据特定 key 提取的子串,例如这里的 http:"..." 形式的子串。

// unpack填充ptr指向的结构的字段

// 从请求中的HTTP请求参数。

func Unpack(req *http.Request, ptr interface{}) error {

if err := req.ParseForm(); err != nil {

return err

}

// 构建由有效名称键控的字段的映射。

fields := make(map[string]reflect.Value)

v := reflect.ValueOf(ptr).Elem() // 结构变量

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

fieldInfo := v.Type().Field(i) // 反射.StructField

tag := fieldInfo.Tag // 一个 reflect.StructTag

name := tag.Get("http")

if name == "" {

name = strings.ToLower(fieldInfo.Name)

}

fields[name] = v.Field(i)

}

// 为请求中的每个参数更新结构字段。

for name, values := range req.Form {

f := fields[name]

if !f.IsValid() {

continue // 忽略无法识别的HTTP参数

}

for _, value := range values {

if f.Kind() == reflect.Slice {

elem := reflect.New(f.Type().Elem()).Elem()

if err := populate(elem, value); err != nil {

return fmt.Errorf("%s: %v", name, err)

}

f.Set(reflect.Append(f, elem))

} else {

if err := populate(f, value); err != nil {

return fmt.Errorf("%s: %v", name, err)

}

}

}

}

return nil

}

最后,Unpack 遍历 HTTP 请求的 name/valu 参数键值对,并且根据更新相应的结构体成员。回想一下,同一个名字的参数可能出现多次。如果发生这种情况,并且对应的结构体成员是一个 slice,那么就将所有的参数添加到 slice 中。其它情况,对应的成员值将被覆盖,只有最后一次出现的参数值才是起作用的。

populate 函数小心用请求的字符串类型参数值来填充单一的成员 v(或者是 slice 类型成员中的单一的元素)。目前,它仅支持字符串、有符号整数和布尔型。

func populate(v reflect.Value, value string) error {

switch v.Kind() {

case reflect.String:

v.SetString(value)

case reflect.Int:

i, err := strconv.ParseInt(value, 10, 64)

if err != nil {

return err

}

v.SetInt(i)

case reflect.Bool:

b, err := strconv.ParseBool(value)

if err != nil {

return err

}

v.SetBool(b)

default:

return fmt.Errorf("unsupported kind %s", v.Type())

}

return nil

}

如果将上面的处理程序添加到一个 web 服务器,则可以产生以下的会话:

$ go build gopl.io/ch12/search

$ ./search &

$ ./fetch 'http://localhost:12345/search'

Search: {Labels:[] MaxResults:10 Exact:false}

$ ./fetch 'http://localhost:12345/search?l=golang&l=programming'

Search: {Labels:[golang programming] MaxResults:10 Exact:false}

$ ./fetch 'http://localhost:12345/search?l=golang&l=programming&max=100'

Search: {Labels:[golang programming] MaxResults:100 Exact:false}

$ ./fetch 'http://localhost:12345/search?x=true&l=golang&l=programming'

Search: {Labels:[golang programming] MaxResults:10 Exact:true}

$ ./fetch 'http://localhost:12345/search?q=hello&x=123'

x: strconv.ParseBool: parsing "123": invalid syntax

$ ./fetch 'http://localhost:12345/search?q=hello&max=lots'

max: strconv.ParseInt: parsing "lots": invalid syntax

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值