一尘不染

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

go

嗨,我想将get查询参数转换为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"`
}

我有一个发送GET请求该情况下,用户可以指定可选参数OffsetLimitSortByAscUsernameFirst_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请求中发送正文,所以解决方案是什么呢?而不是单独获取每个参数然后将其放入结构中?


阅读 232

收藏
2020-07-02

共1个答案

一尘不染

使用大猩猩的schema包装

github.com/gorilla/schema包装正是为此而发明的。

您可以使用struct标记]来告诉如何将URL参数映射到struct字段,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.Form是一个mapstring到的映射[]string(因为一个参数可能会在URL中多次列出:

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.Formmap单个string值而不是[]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结构并将其编入结构。

2020-07-02