如何将所有GET请求查询参数放入Go中的结构中?

时间:2016-09-14 09:18:07

标签: http go struct query-parameters

您好我想将查询参数转换为Go中的结构,例如我有这样的结构:

type Filter struct {
    Offset int64  `json:"offset"`
    Limit  int64  `json:"limit"`
    SortBy string `json:"sortby"`
    Asc    bool   `json:"asc"`

    //User specific filters
    Username   string `json:"username"`
    First_Name string `json:"first_name"`
    Last_Name  string `json:"last_name"`
    Status     string `json:"status"`
}

我有可选参数,用户可以在发送OffsetLimitSortByAscUsername的请求时指定这些参数,First_NameLast_NameStatus

如果这些参数是在体内发送的,那么我会这样做:

b, err := ioutil.ReadAll(r.Body)
if err != nil {

    log.WithFields(logFields).Errorf("Reading Body Message:failed:%v", err)

    return
}

var filter Filter
err = json.Unmarshal(b, &filter)

但是我无法在GET请求中发送正文,那么解决方案是什么,而不是单独获取每个参数然后将它们放入结构中?

3 个答案:

答案 0 :(得分:5)

使用大猩猩的schema

github.com/gorilla/schema软件包是为此而发明的。

您可以使用struct tags告诉如何将网址参数映射到结构字段,schema包会查找"schema"标记键。

使用它:

import "github.com/gorilla/schema"

type Filter struct {
    Offset int64  `schema:"offset"`
    Limit  int64  `schema:"limit"`
    SortBy string `schema:"sortby"`
    Asc    bool   `schema:"asc"`

    //User specific filters
    Username   string `schema:"username"`
    First_Name string `schema:"first_name"`
    Last_Name  string `schema:"last_name"`
    Status     string `schema:"status"`
}

func MyHandler(w http.ResponseWriter, r *http.Request) {
    if err := r.ParseForm(); err != nil {
        // Handle error
    }

    filter := new(Filter)
    if err := schema.NewDecoder().Decode(filter, r.Form); err != nil {
        // Handle error
    }

    // Do something with filter
    fmt.Printf("%+v", filter)
}

使用json

进行封送和解组

请注意,schema包也会尝试将参数值转换为字段类型。

如果结构只包含[]string类型的字段(或者您愿意做出妥协),则可以在没有schema包的情况下执行此操作。

Request.Formmap,从string[]string的映射(因为一个参数可能会在网址中多次列出:

Form url.Values

url.Values

type Values map[string][]string

例如,如果您的Filter结构看起来像这样:

type Filter struct {
    Offset []string `json:"offset"`
    Limit  []string `json:"limit"`
    SortBy []string `json:"sortby"`
    // ..other fields
}

您只需使用json包来封送r.Form,然后将其解组到您的结构中:

func MyHandler(w http.ResponseWriter, r *http.Request) {
    if err := r.ParseForm(); err != nil {
        // Handle error
    }
    data, err := json.Marshal(r.Form)
    if err != nil {
        // Handle error
    }
    filter := new(Fiter)
    if err = json.Unmarshal(data, filter); err != nil {
        // Handle error
    }
    fmt.Printf("%+v", filter)
}

此解决方案处理是否为同一参数名称提供了多个值。如果你不关心多个价值观而你只想要一个,那么你首先要做的是转变" r.Form改为mapstring个值而不是[]string

这就是它的样子:

type Filter struct {
    Offset string `json:"offset"`
    Limit  string `json:"limit"`
    SortBy string `json:"sortby"`
    // ..other fields
}

// Transformation from map[string][]string to map[string]string:
m := map[string]string{}
for k, v := range r.Form {
    m[k] = v[0]
}

然后你可以将m编组并以同样的方式将其解组到Filter结构中。

答案 1 :(得分:0)

对于Echo框架,您可以将查询字符串绑定到类似于此示例代码的结构。

type Filter struct {
    Offset int64  `query:"offset"`
    Limit  int64  `query:"limit"`
    SortBy string `query:"sortby"`
    Asc    bool   `query:"asc"`

    //User specific filters
    Username   string `query:"username"`
    First_Name string `query:"first_name"`
    Last_Name  string `query:"last_name"`
    Status     string `query:"status"`
}

query := new(Filter)
if err := c.Bind(query); err != nil {
    // handle error here
}

答案 2 :(得分:0)

还有另一个替代包 httpin

支持:

  1. 从多个来源解码,例如HTTP 查询和标头。
  2. 为一个字段定义多个键,例如为同一字段查找 per_pagepage_size
  3. 能够为您的字段实施自定义“指令”以扩展用途。
  4. ...等等

示例代码:

type Authorization struct {
    // Decode from multiple sources, the former with higher priority
    Token string `in:"form=access_token;header=x-api-token;required"`
}

type Pagination struct {
    Page int `in:"form=page"`

    // Decode from multiple keys in the same source, the former with higher priority
    PerPage int `in:"form=per_page,page_size"`
}

type ListUsersInput struct {
    Gender   string `in:"form=gender"`
    AgeRange []int  `in:"form=age_range"`
    IsMember bool   `in:"form=is_member"`

    Pagination    // Embedded field works
    Authorization // Embedded field works
}

func ListUsers(rw http.ResponseWriter, r *http.Request) {
    inputInterface, err := httpin.New(ListUsersInput{}).Decode(r)
    if err != nil {
        // Error occurred, `err` can be type of *httpin.InvalidFieldError
        // Do sth.
        return
    }

    input := interfaceInput.(*ListUsersInput)
    // Do sth.
}