如何从 Go 中的查询参数中提取逗号分隔值?

How to extract comma separated values from query parameter in Go?

我正在使用 Gin Web Framework 并且我正在尝试找到一种方法来将查询参数中的逗号分隔值列表绑定到结构中。以下是我的代码片段:

type QueryParams struct {
    Type            []string `form:"type"`
}


func BulkRead(c *gin.Context) {

    params := QueryParams{
        Type:            []string{},
    }

    if err := c.ShouldBindQuery(&params); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't bind query params"})
        return
    }
    
    c.Status(200)
}

要求:GET /api/v1/car?type=ford,audi

我的期望:["ford", "audi"]

我得到的是:"ford,audi"

有没有简单的方法来做到这一点?或者我需要编写一个自定义函数来处理这个问题吗?

可能没有这种方法。 通常我这样做:

type QueryParams struct {
    Type string `form:"type"`
}

func (q *QueryParams) Types() []string {
    return strings.Split(q.Type, ",")
}

func BulkRead(c *gin.Context) {
    params := new(QueryParams)

    if err := c.ShouldBindQuery(&params); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't bind query params"})
        return
    }
    c.JSONP(200, map[string]interface{}{
        "types": params.Types(),
    })
}

它可能对你也有帮助。

package main

import (
    "log"
    "net/http"
    "strings"

    "github.com/gin-gonic/gin"
)

type TypeBinding struct {
    name string
}

func NewTypeBinding(name string) *TypeBinding {
    return &TypeBinding{
        name: name,
    }
}

func (t *TypeBinding) Name() string {
    return t.name
}

func (t *TypeBinding) Bind(r *http.Request, i interface{}) error {
    ttype := r.URL.Query().Get(t.name)

    ii := i.(*QueryParams)

    ii.Type = strings.Split(ttype, ",")

    return nil
}

type QueryParams struct {
    Type []string `url:"type"`
}

func ggin() {
    router := gin.Default()

    typeBinding := NewTypeBinding("type")
    var opt QueryParams

    router.GET("/", func(c *gin.Context) {
        if err := c.MustBindWith(&opt, typeBinding); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't bind query params"})
            return
        }

        log.Printf("type: %v", opt.Type)
        c.String(http.StatusOK, "type: %s", opt.Type)
    })
    router.Run(":8080")
}

Gin 无法为您执行此操作。最简单的解决方案是只获取查询参数并自己拆分。这实际上是 2 行代码:

func MyHandler(c *gin.Context) {
    ss := strings.Split(c.Query("type"), ",")
    fmt.Println(ss) // [ford audi]

    qp := QueryParams{
        Type: ss,
    }
}

如果您可以选择更改发出请求的方式,请将其更改为:

GET /api/v1/car?type=ford&type=audi(重复查询键)

或URL-对逗号进行编码:

GET /api/v1/car?type=ford%20audi (, -> %20)

然后 context.GetQueryArray 将按您的预期工作:

func MyHandler(c *gin.Context) {
    cars, _ := c.GetQueryArray("type")
    fmt.Println(cars) // [ford audi]
}

当然,实现您自己的 binding.Binding 是一种选择,适用于您拥有的不仅仅是该查询参数并且希望将绑定逻辑封装到一个地方的情况,但我觉得这对您的用例来说太过分了:

type commaSepQueryBinding struct {}

func (commaSepQueryBinding) Name() string {
    return "comma-sep-query"
}

func (commaSepQueryBinding) Bind(req *http.Request, obj interface{}) error {
    values := req.URL.Query()
    p := obj.(*QueryParams)
    p.Type = strings.Split(values["type"][0], ",")
    return nil
}

func MyHandler(c *gin.Context) {
    q := QueryParams{}
    err := c.ShouldBindWith(&q, &commaSepQueryBinding{})
    if err != nil {
        panic(err)
    }
    fmt.Println(q.Type) // [ford audi]
}

要从查询参数中提取逗号分隔值,您可以使用 strings 包中的 Split() 方法。我为您的场景创建了一个简单的程序,如下所示:

package main

import (
    "fmt"
    "strconv"
    "strings"
)

func main() {

    var cars []string
    qryResult := "ford,audi"
    carBrands := strings.Split(qryResult, ",")
    fmt.Println(carBrands)

    for i := 0; i < len(carBrands); i++ {

        cars = append(cars, strconv.Quote(carBrands[i]))
    }

    fmt.Println(cars)

}

输出:

[ford audi]
["ford" "audi"]