Reputation:
I am using the Gin Web Framework and I am trying to find a way to bind a list of comma separated values from a query parameter into a struct. The following is a snippet of my code:
type QueryParams struct {
Type []string `form:"type"`
}
func BulkRead(c *gin.Context) {
params := QueryParams{
Type: []string{},
}
if err := c.ShouldBindQuery(¶ms); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't bind query params"})
return
}
c.Status(200)
}
Request: GET /api/v1/car?type=ford,audi
What I expect: ["ford", "audi"]
What I am getting: "ford,audi"
Is there an easy way to do this? Or will I need to write a custom function to handle this?
Upvotes: 5
Views: 6745
Reputation: 751
To extract comma separated values from query parameter you can make use of Split()
method from strings
package .I have created a simple program for your scenario as follows :
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)
}
Output:
[ford audi]
["ford" "audi"]
Upvotes: 3
Reputation: 44697
Gin doesn't have a way to do this for you. The easiest solution is to just get the query param and split it yourself. This is literally 2 lines of code:
func MyHandler(c *gin.Context) {
ss := strings.Split(c.Query("type"), ",")
fmt.Println(ss) // [ford audi]
qp := QueryParams{
Type: ss,
}
}
If you have the option to change how the request is made, change it to:
GET /api/v1/car?type=ford&type=audi
(repeat the query key)
or URL-encode the comma:
GET /api/v1/car?type=ford%20audi
(,
-> %20
)
Then context.GetQueryArray
will work as you expect:
func MyHandler(c *gin.Context) {
cars, _ := c.GetQueryArray("type")
fmt.Println(cars) // [ford audi]
}
Of course implementing your own binding.Binding
is an option, suited if you have more than just that query parameter and want to encapsulate the binding logic into one place, but I feel it's overkill for your use case:
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]
}
Upvotes: 0
Reputation: 34
It might be helpful for you too.
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")
}
Upvotes: -1
Reputation: 636
There may not be such a way. Usually I do this:
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(¶ms); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't bind query params"})
return
}
c.JSONP(200, map[string]interface{}{
"types": params.Types(),
})
}
Upvotes: 1