Stop accessing query strings and repeatedly parsing them into your preferred values - queryparam
can do that for you!
go get -u github.com/tomwright/queryparam
Please use the latest major version. This requires the /v4
at the end of the import as per the go mod documentation.
import github.com/tomwright/queryparam/v4
For examples see godoc examples.
Transform your http handlers from this...
func searchUsersHandler(r *http.Request, rw http.ResponseWriter) {
values := r.URL.Query()
userIDs := make([]string, 0)
if userIDsStr := values.Get("id"); userIDsStr != "" {
userIDs = strings.Split(userIDsStr, ",")
}
teamIDs := make([]string, 0)
if teamIDsStr := values.Get("team-id"); teamIDsStr != "" {
teamIDs = strings.Split(teamIDsStr, ",")
}
mustBeActive := false
switch strings.ToLower(values.Get("must-be-active")) {
case "true", "yes", "y":
mustBeActive = true
case "":
break
default:
// unhandled bool value... handle as 400 or ignore to default as false
}
createdAfter := time.Time{}
if createdAfterStr := values.Get("must-be-active"); createdAfterStr != "" {
var err error
createdAfter, err = time.Parse(time.RFC3339, createdAfterStr)
if err != nil {
// bad time value
}
}
users, err := searchUsers(userIDs, teamIDs, mustBeActive, createdAfter)
// handle users and err...
}
To this...
func searchUsersHandler(r *http.Request, rw http.ResponseWriter) {
req := struct {
UserIDs []string `queryparam:"id"`
TeamIDs []string `queryparam:"team-id"`
MustBeActive bool `queryparam:"must-be-active"`
CreatedAfter time.Time `queryparam:"created-after"`
}{}
err := queryparam.Parse(r.URL.Query(), &req)
switch err {
case nil:
break
case queryparam.ErrInvalidBoolValue: // only necessary if the request contains a bool value
// they have entered a non-bool value.
// this can be handled this as a 400 or ignored to default to false.
return
default:
// something went wrong when parsing a value.
// send a 500.
return
}
users, err := searchUsers(req.UserIDs, req.TeamIDs, req.MustBeActive, req.CreatedAfter)
// handle users and err...
}
By default queryparam
can parse the following types.
string
[]string
int
int32
int64
float32
float64
bool
time.Time
queryparam.Present
You can add custom type parsers and setters with the following:
// your custom type.
type MyCustomStringType string
// add a value parser for the custom type.
queryparam.DefaultParser.ValueParsers[reflect.TypeOf(MyCustomStringType(""))] = func(value string, _ string) (reflect.Value, error) {
return reflect.ValueOf(MyCustomStringType(value)), nil
}
You can override the default value parsers in a similar manner...
queryparam.DefaultParser.ValueParsers[reflect.TypeOf("")] = func(value string, _ string) (reflect.Value, error) {
// my custom string parser
return reflect.ValueOf(value), nil
}