Как извлечь значения, разделенные запятыми, из параметра запроса в Go?

# #http #go #query-parameters #go-gin

Вопрос:

Я использую веб-фреймворк Gin и пытаюсь найти способ связать список значений, разделенных запятыми, из параметра запроса в структуру. Ниже приведен фрагмент моего кода:

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


func BulkRead(c *gin.Context) {

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

    if err := c.ShouldBindQuery(amp;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"

Есть ли простой способ сделать это? Или мне нужно будет написать пользовательскую функцию для обработки этого?

Комментарии:

1. Я думаю, вам, вероятно, нужно будет использовать GetQueryArray .

2. Я попробовал это сделать, но это все равно не дает мне того результата, который я ищу, arr, _ := c.GetQueryArray("type"); fmt.Println(arr[0]) — > "ford,audi"

3. Функция никак не может узнать, ожидаете ли вы несколько значений, разделенных запятой (почему не двоеточие или другой символ?). Возможно, следующий запрос будет автоматически работать в любом http-маршрутизаторе: GET /api/v1/cart?type=fordamp;type=audi

4. Возможно, rsql является более подходящим решением для вашего API

Ответ №1:

Для извлечения значений, разделенных запятыми, из параметра запроса вы можете использовать Split() метод из strings пакета .Я создал простую программу для вашего сценария следующим образом :

 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=fordamp;type=audi (повторите ключ запроса)

или URL-кодируйте запятую:

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

Тогда 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(amp;q, amp;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(amp;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 amp;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(amp;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")
}