2021-04-13 07:33:04 +02:00
# httpin
2021-04-22 12:45:15 +02:00
[![codecov ](https://codecov.io/gh/ggicci/httpin/branch/main/graph/badge.svg?token=RT61L9ngHj )](https://codecov.io/gh/ggicci/httpin)
2021-05-08 10:15:26 +02:00
HTTP Input for Go - Decode an HTTP request into a custom struct
**Define your input struct and then fetch your data!**
2021-04-29 05:55:02 +02:00
2021-05-08 08:07:09 +02:00
## Quick View
2021-04-29 05:55:02 +02:00
2021-05-08 08:07:09 +02:00
< table >
< tr >
2021-05-08 10:15:26 +02:00
< th > BEFORE (use net/http)< / th >
< th > AFTER (use httpin)< / th >
2021-05-08 08:07:09 +02:00
< / tr >
< tr >
< td >
2021-04-29 05:55:02 +02:00
2021-05-08 08:07:09 +02:00
```go
func ListUsers(rw http.ResponseWriter, r *http.Request) {
page, err := strconv.ParseInt(r.FormValue("page"), 10, 64)
if err != nil {
// ... invalid page
return
}
perPage, err := strconv.ParseInt(r.FormValue("per_page"), 10, 64)
if err != nil {
// ... invalid per_page
return
}
isVip, _ := strconv.ParseBool(r.FormValue("is_vip"))
// do sth.
}
```
2021-04-29 05:55:02 +02:00
2021-05-08 08:07:09 +02:00
< / td >
< td >
```go
type ListUsersInput struct {
Page int `in:"form=page"`
PerPage int `in:"form=per_page"`
IsVip bool `in:"form=is_vip"`
}
func ListUsers(rw http.ResponseWriter, r *http.Request) {
2021-05-08 09:27:37 +02:00
interfaceInput, err := httpin.New(ListUsersInput{}).Decode(r)
2021-05-08 08:07:09 +02:00
if err != nil {
// err can be *httpin.InvalidField
return
}
input := interfaceInput.(*ListUsersInput)
// do sth.
}
```
< / td >
< / tr >
< / table >
## Features
2021-05-08 10:33:23 +02:00
- [x] Builtin directive `form` to decode a field from HTTP query, i.e. `http.Request.Form`
- [x] Builtin directive `header` to decode a field from HTTP headers, e.g. `http.Request.Header`
- [x] Builtin decoders used by `form` and `header` directives for basic types, e.g. `bool` , `int` , `int64` , `float32` , `time.Time` , ... [full list ](./decoders.go )
- [x] Decode a field by inspecting a set of keys from the same source
- [x] Decode a field from multiple sources, e.g. both query and headers
- [ ] Register or replace decoders for both builtin basic types and custom types
2021-05-08 08:07:09 +02:00
- [x] Define input struct with embedded struct fields
2021-05-08 10:33:23 +02:00
- [x] Builtin directive `required` to tag a field as **required**
2021-05-08 08:07:09 +02:00
- [ ] Builtin encoders for basic types
2021-05-08 10:33:23 +02:00
- [ ] Register or replace encoders for both builtin basic types and custom types
- [x] Register custom directive executors to extend the field resolving abilities, see directive [required ](./required.go ) as an example and think about implementing your own directives like `trim` , `to_lowercase` , `base58_to_int` , etc.
2021-05-08 08:07:09 +02:00
## Sample User Defined Input Structs
2021-04-29 05:55:02 +02:00
```go
type Authorization struct {
2021-05-08 08:07:09 +02:00
// Decode from multiple sources, the former with higher priority
2021-05-06 10:06:55 +02:00
Token string `in:"form=access_token,header=x-api-token"`
2021-04-29 05:55:02 +02:00
}
type Pagination struct {
2021-05-08 08:07:09 +02:00
Page int `in:"form=page"`
// Decode from multiple keys in the same source, the former with higher priority
2021-05-06 10:06:55 +02:00
PerPage int `in:"form=per_page,page_size"`
2021-04-29 05:55:02 +02:00
}
2021-05-08 08:07:09 +02:00
type ListUsersInput struct {
2021-05-06 10:06:55 +02:00
Gender string `in:"form=gender"`
AgeRange []int `in:"form=age_range"`
IsMember bool `in:"form=is_member"`
2021-04-29 05:55:02 +02:00
2021-05-08 08:07:09 +02:00
Pagination // Embedded field works
Authorization // Embedded field works
2021-04-29 05:55:02 +02:00
}
```
2021-05-08 08:07:09 +02:00
## Advanced - Use Middleware
2021-04-29 05:55:02 +02:00
2021-05-08 08:11:26 +02:00
First, set up the middleware for your handlers. We recommend using [alice ](https://github.com/justinas/alice ) to chain your HTTP middleware functions.
2021-04-29 05:55:02 +02:00
```go
2021-05-08 08:07:09 +02:00
func init() {
mux.Handle("/users", alice.New(
httpin.NewInput(ListUsersInput{}),
).ThenFunc(ListUsers)).Methods("GET")
}
2021-04-29 05:55:02 +02:00
```
2021-05-08 08:07:09 +02:00
Second, fetch your input with **only one line** of code.
2021-04-29 05:55:02 +02:00
```go
2021-05-08 08:07:09 +02:00
func ListUsers(rw http.ResponseWriter, r *http.Request) {
input := r.Context().Value(httpin.Input).(*UserQuery)
// do sth.
2021-04-29 05:55:02 +02:00
}
```