【问题标题】:How to extract comma separated values from query parameter in Go?如何从 Go 中的查询参数中提取逗号分隔值?
【发布时间】:2021-07-09 21:52:37
【问题描述】:

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

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"

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

【问题讨论】:

  • 我想你可能需要使用GetQueryArray
  • 我试了一下,但仍然没有给我想要的结果,arr, _ := c.GetQueryArray("type"); fmt.Println(arr[0]) -> "ford,audi"
  • 该函数无法知道您期望多个用逗号分隔的值(为什么不是冒号或其他字符?)。也许以下请求将在任何 http 路由器中自动运行:GET /api/v1/cart?type=ford&type=audi
  • 也许 rsql 是更适合您的 API 的解决方案

标签: http go query-parameters go-gin


【解决方案1】:

要从查询参数中提取逗号分隔值,您可以使用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"]

【讨论】:

    【解决方案2】:

    杜松子酒无法为您做到这一点。最简单的解决方案是获取查询参数并自行拆分。这实际上是 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&amp;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]
    }
    

    【讨论】:

      【解决方案3】:

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

      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(),
          })
      }
      

      【讨论】:

        【解决方案4】:

        也许对你也有帮助。

        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")
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-26
          • 1970-01-01
          • 1970-01-01
          • 2015-03-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多