.github/workflows | ||
internal | ||
patch | ||
.gitignore | ||
body_test.go | ||
body.go | ||
chain_test.go | ||
chain.go | ||
decoder_test.go | ||
decoders.go | ||
directives_test.go | ||
directives.go | ||
errors.go | ||
extractor.go | ||
form.go | ||
go.mod | ||
go.sum | ||
gorilla_test.go | ||
gorilla.go | ||
header.go | ||
httpin_test.go | ||
httpin.go | ||
LICENSE | ||
Makefile | ||
options_test.go | ||
options.go | ||
README.md | ||
required.go | ||
resolver_test.go | ||
resolver.go |
httpin
HTTP Input for Go - Decode an HTTP request into a custom struct
Define the struct for your input and then fetch your data!
Quick View
BEFORE (use net/http) | AFTER (use httpin) |
---|---|
|
|
Features
- Builtin directive
form
to decode a field from HTTP query (URL params), i.e.http.Request.Form
- Builtin directive
header
to decode a field from HTTP headers, i.e.http.Request.Header
- Builtin decoders used by
form
andheader
directives for basic types, e.g.bool
,int
,int64
,float32
,time.Time
, ... full list - Decode a field by inspecting a set of keys from the same source, e.g.
in:"form=per_page,page_size"
- Decode a field from multiple sources, e.g. both query and headers,
in:"form=access_token;header=x-api-token"
- Register custom type decoders by implementing
httpin.Decoder
interface - Compose an input struct by embedding struct fields
- Builtin directive
required
to tag a field as required - Register custom directive executors to extend the ability of field resolving, see directive required as an example and think about implementing your own directives like
trim
,to_lowercase
,base58_to_int
, etc. - Easily integrating with popular Go web frameworks and packages
Sample User Defined Input Structs
type Authorization struct {
// Decode from multiple sources, the former with higher priority
Token string `in:"form=access_token;header=x-api-token;required"`
}
type Pagination struct {
Page int `in:"form=page"`
// Decode from multiple keys in the same source, the former with higher priority
PerPage int `in:"form=per_page,page_size"`
}
type ListUsersInput struct {
Gender string `in:"form=gender"`
AgeRange []int `in:"form=age_range"`
IsMember bool `in:"form=is_member"`
Pagination // Embedded field works
Authorization // Embedded field works
}
Integrate with Go Native http.Handler (Use Middleware)
First, set up the middleware for your handlers (bind Input vs. Handler). We recommend using alice to chain your HTTP middleware functions.
func init() {
http.Handle("/users", alice.New(
httpin.NewInput(ListUsersInput{}),
).ThenFunc(ListUsers))
}
Second, fetch your input with only ONE LINE of code.
func ListUsers(rw http.ResponseWriter, r *http.Request) {
input := r.Context().Value(httpin.Input).(*ListUsersInput)
// Do sth.
}
Integrate with Popular Go Web Frameworks and Components
Advanced
🔥 Extend httpin
by adding custom directives
Know the concept of a Directive
:
type Authorization struct {
Token string `in:"form=access_token,token;header=x-api-token;required"`
^---------------------^ ^----------------^ ^------^
d1 d2 d3
}
There are three directives above, separated by semicolons (;
):
- d1:
form=access_token,token
- d2:
header=x-api-token
- d3:
required
A directive consists of two parts separated by an equal sign (=
). The left part is the name of an executor who was designed to run this directive. The right part is a list of arguments separated by commas (,
) which will be passed to the corresponding executor at runtime.
For instance, form=access_token,token
, here form
is the name of the executor, and access_token,token
is the argument list which will be parsed as []string{"access_token", "token"}
.
There are several builtin directive executors, e.g. form
, header
, required
, ... full list. And you can define your own by:
First, create a directive executor by implementing the httpin.DirectiveExecutor
interface:
func toLower(ctx *DirectiveContext) error {
if ctx.ValueType.Kind() != reflect.String {
return errors.New("not a string")
}
currentValue := ctx.Value.Elem().String()
newValue := strings.ToLower(currentValue)
ctx.Value.Elem().SetString(newValue)
return nil
}
// Adapt toLower to httpin.DirectiveExecutor.
var MyLowercaseDirectiveExecutor = httpin.DirectiveExecutorFunc(toLower)
Seconds, register it to httpin
:
httpin.RegisterDirectiveExecutor("to_lowercase", MyLowercaseDirectiveExecutor)
Finally, you can use your own directives in the struct tags:
type Authorization struct {
Token string `in:"form=token;required;to_lowercase"`
}
The directives will run in the order as they defined in the struct tag.