将 URL.Query(切片映射)转换为 struct golang

Posted

技术标签:

【中文标题】将 URL.Query(切片映射)转换为 struct golang【英文标题】:Convert URL.Query (map of slices) to struct golang 【发布时间】:2017-03-26 16:18:22 【问题描述】:

如果能从标准库 URL.Query() 直接映射到结构体,那就太棒了。

Query() 返回如下地图: map[a:[aaaa] b:[bbbb] c:[cccc]]

结构如下:

type Thing struct 
    A    string
    B    string
    C    string

我不知道为什么 URL.Query 会返回一个包含数组元素的映射。 (嗯..我知道why 但GET 不太可能有重复的参数)

【问题讨论】:

一个 GET 是 -- 不太可能 -- 可以有重复的参数。在这种情况下,它被转换为一个值切片。你看过 gorilla.schema 包吗?我相信它可以胜任。 在我的情况下,我会很好并且热衷于在重复的情况下触发一个异常。我查看了 gorilla.schema,太棒了!谢谢。 【参考方案1】:

请在下面找到直接在 golang 结构中解析 get 查询参数然后将结构作为响应发送回来的完整示例

package main

import (
    "log"
    "net/http"
    "encoding/json"
    "github.com/gorilla/schema"
)

var decoder  = schema.NewDecoder()

type EmployeeStruct struct 
    MemberId         string `schema:"memberId"`
    ActivityType     string `schema:"activityType"`
    BusinessUnitCode int    `schema:"businessUnitCode"`


func GetEmployee(w http.ResponseWriter, r *http.Request) 
    var employeeStruct EmployeeStruct

    err := decoder.Decode(&employeeStruct, r.URL.Query())
    if err != nil 
        log.Println("Error in GET parameters : ", err)
     else 
        log.Println("GET parameters : ", employeeStruct)
    

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(employeeStruct)


func main() 
    mux := http.NewServeMux()
    mux.HandleFunc("/GetEmployee", GetEmployee)
    log.Fatal(http.ListenAndServe(":8080", mux))

执行和测试的步骤(假设您将上述代码保存在 employee.go 中):

第一步:运行employee.go

第二步:在浏览器中打开http://localhost:8080/GetEmployee?memberId=123&activityType=Call&businessUnitCode=56

第 3 步:您应该在浏览器窗口中得到以下响应


    "MemberId": "123",
    "ActivityType": "Call",
    "BusinessUnitCode": 56

第 4 步:在控制台上,您应该会看到以下内容

GET parameters :  123 Call 56

【讨论】:

【参考方案2】:

示例:

filters="reference":["docker.io/library/alpine:latest"]

需要将网址编码为:

filters=%7B%22reference%22%3A%5B%22docker.io%2Flibrary%2Falpine%3Alatest%22%5D%7D

并且可以使用"github.com/gorilla/schema"

    query := struct 
        All     bool
        Filters map[string][]string `schema:"filters"`
        Digests bool
        Filter  string 
    
    decoder := schema.NewDecoder()
    decoder.Decode(&query, r.URL.Query())

【讨论】:

【参考方案3】:

正如@mh-cbon 所指出的,gorilla schema 是这里的终极解决方案。

而不是从 URL 属性中获取 queryParams。

func handleRequest(w http.ResponseWriter, r *http.Request) 
    queryString := r.URL.Query()
    //...parsing the Values -> map[string][]string

gorilla 模式的方法是将r.PostForm 发送到解码函数。

func handleRequest(w http.ResponseWriter, r *http.Request) 
    err := decoder.Decode(person, r.PostForm)
    //...using reflect each struct's property can be called using 
    // the PostForm(url string, data url.Values) signature

    fmt.Print(person.GoodJobGorilla)

【讨论】:

【参考方案4】:

我写了一个 Go 包 ggicci/httpin 专门用于解决这个问题。不仅用于查询,还可以解码来自 HTTP 标头的数据。这是一个例子:

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.

我确实希望这个库可以节省大家用 Go 编写 API 的时间。

【讨论】:

【参考方案5】:

你可以使用Echo的优雅包。

我写了一些代码作为例子,带有不言自明的cmets

 package main

 import (
     "log"
     "github.com/labstacks/echo"
)

// Declare your struct with form: "" tag
type Employee struct 
    MemberId         string `form:"memberId"`
    ActivityType     string `form:"activityType"`
    BusinessUnitCode int    `form:"businessUnitCode"`


// Your handlers should look like this method 
// Which takes an echo.Context and returns an error
func GetEmployee(ctx echo.Context) error
    var employee Employee
    // With Bind, you can get the Post Body or query params from http.Request
    // that is wrapped by echo.Context here 
    if err := ctx.Bind(&employee);err != nil 
        return err
     

    // now you can use your struct , e.g
    return ctx.json(200, employee.MemberId)


// now use the handler in your main function or anywhere you need
func main() 
    e := echo.New()
    e.Get("/employee", GetEmployee)
    log.Fatal(e.Start(":8080"))

【讨论】:

我认为这是相关文档pkg.go.dev/github.com/labstack/echo/v4#DefaultBinder.Bind 的正确链接但是,事实是,IDK,上下文使用的 Binder 和其他。所以我认为它可能存在更好的链接。【参考方案6】:

只需将字符串解析为 URL,然后您就可以使用 lib github.com/gorilla/schema 对其进行解析:)

// Example to parse querystring to struct
package main

import (
    "log"
    "net/url"

    "github.com/gorilla/schema"
)

type URLParams struct 
    Code  string `schema:"code"`
    State string `schema:"state"`


func main() 
    var (
        params  URLParams
        decoder = schema.NewDecoder()
    )
    p := "https://www.redirect-url.com?code=CODE&state=RANDOM_ID"

    u, _ := url.Parse(p)

    err := decoder.Decode(&params, u.Query())
    if err != nil 
        log.Println("Error in Decode parameters : ", err)
     else 
        log.Printf("Decoded parameters : %#v\n", params)
    


https://go.dev/play/p/CmuPhdKh6Yg

【讨论】:

以上是关于将 URL.Query(切片映射)转换为 struct golang的主要内容,如果未能解决你的问题,请参考以下文章

golang超级mapper包 - coven

将 []byte 切片转换为 []int 切片

为啥可以将切片分配给空接口但不能将其强制转换为相同的空接口

如何将切片转换为数组引用?

将 numpy 切片转换为 Opencv c++ Mat

如何将字节切片 (&[u8]) 的缓冲区转换为整数?