How to get all GET request query parameters into a structure in Go?
For Echo
framework, you can bind a query string to a structure like this sample code.
type Filter struct {
Offset int64 `query:"offset"`
Limit int64 `query:"limit"`
SortBy string `query:"sortby"`
Asc bool `query:"asc"`
//User specific filters
Username string `query:"username"`
First_Name string `query:"first_name"`
Last_Name string `query:"last_name"`
Status string `query:"status"`
}
query := new(Filter)
if err := c.Bind(query); err != nil {
// handle error here
}
Using ggicci/httpin
Disclaimer: I'm the creator and maintainer of this package.
httpin helps you easily decoding HTTP request data from
- Query parameters, e.g.
?name=john&is_member=true
- Headers, e.g.
Authorization: xxx
- Form data, e.g.
username=john&password=******
- JSON/XML Body, e.g.
POST {"name":"john"}
- Path variables, e.g.
/users/{username}
- File uploads
How to use?
type ListUsersInput struct {
Page int `in:"query=page"`
PerPage int `in:"query=per_page"`
IsMember bool `in:"query=is_member"`
}
func ListUsers(rw http.ResponseWriter, r *http.Request) {
input := r.Context().Value(httpin.Input).(*ListUsersInput)
if input.IsMember {
// Do sth.
}
// Do sth.
}
httpin is:
- well documented: at https://ggicci.github.io/httpin/
- well tested: coverage over 98%
- open integrated: with net/http, go-chi/chi, gorilla/mux, gin-gonic/gin, etc.
- extensible (advanced feature): by adding your custom directives. Read httpin - custom directives for more details.
- awesome mentioned: https://github.com/avelino/awesome-go#forms
Using gorilla's schema
package
The github.com/gorilla/schema
package was invented exactly for this.
You can use struct tags to tell how to map URL parameters to struct fields, the schema
package looks for the "schema"
tag keys.
Using it:
import "github.com/gorilla/schema"
type Filter struct {
Offset int64 `schema:"offset"`
Limit int64 `schema:"limit"`
SortBy string `schema:"sortby"`
Asc bool `schema:"asc"`
//User specific filters
Username string `schema:"username"`
First_Name string `schema:"first_name"`
Last_Name string `schema:"last_name"`
Status string `schema:"status"`
}
func MyHandler(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
// Handle error
}
filter := new(Filter)
if err := schema.NewDecoder().Decode(filter, r.Form); err != nil {
// Handle error
}
// Do something with filter
fmt.Printf("%+v", filter)
}
Marshaling and unmarshaling using json
Note that the schema
package will also try to convert parameter values to the type of the field.
If the struct would only contain fields of []string
type (or you're willing to make that compromise), you can do that without the schema
package.
Request.Form
is a map
, mapping from string
to []string
(as one parameter may be listed multiple times in the URL:
Form url.Values
And url.Values
:
type Values map[string][]string
So for example if your Filter
struct would look like this:
type Filter struct {
Offset []string `json:"offset"`
Limit []string `json:"limit"`
SortBy []string `json:"sortby"`
// ..other fields
}
You could simply use the json
package to marshal r.Form
, then unmarshal it into your struct:
func MyHandler(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
// Handle error
}
data, err := json.Marshal(r.Form)
if err != nil {
// Handle error
}
filter := new(Fiter)
if err = json.Unmarshal(data, filter); err != nil {
// Handle error
}
fmt.Printf("%+v", filter)
}
This solution handles if multiple values are provided for the same parameter name. If you don't care about multiple values and you just want one, you first have to "transform" r.Form
to a map
with single string
values instead of []string
.
This is how it could look like:
type Filter struct {
Offset string `json:"offset"`
Limit string `json:"limit"`
SortBy string `json:"sortby"`
// ..other fields
}
// Transformation from map[string][]string to map[string]string:
m := map[string]string{}
for k, v := range r.Form {
m[k] = v[0]
}
And then you can marshal m
and unmarshal into it into the Filter
struct the same way.