1
0
mirror of https://github.com/ggicci/httpin.git synced 2024-11-24 08:32:45 +02:00
🍡 HTTP Input for Go - Decode an HTTP request into a custom struct 📢 the encoding feature will come soon, see branch feat/encoder https://ggicci.github.io/httpin/
Go to file
2021-05-08 16:33:23 +08:00
.github/workflows refactor: update ci scripts 2021-04-23 11:26:31 +08:00
internal refactor: add internal package to reduce public API surface 2021-05-08 15:27:37 +08:00
patch refactor: add internal package to reduce public API surface 2021-05-08 15:27:37 +08:00
.gitignore Initial commit 2021-04-13 10:15:37 +08:00
body_test.go refactor: move httpin_test package to httpin 2021-04-29 11:07:43 +08:00
body.go refactor: add internal package to reduce public API surface 2021-05-08 15:27:37 +08:00
chain.go refactor: remove Middleware type 2021-05-08 15:48:17 +08:00
debug.go feat: debug switch, default UTC time and up tests 2021-05-06 16:06:55 +08:00
decoders.go refactor: add internal package to reduce public API surface 2021-05-08 15:27:37 +08:00
directives_test.go feat: can replace a directive executor 2021-05-08 15:56:43 +08:00
directives.go feat: can replace a directive executor 2021-05-08 15:56:43 +08:00
errors.go chore(docs): update README 2021-05-08 14:07:09 +08:00
form.go refactor: add internal package to reduce public API surface 2021-05-08 15:27:37 +08:00
go.mod refactor: move httpin_test package to httpin 2021-04-29 11:07:43 +08:00
go.sum refactor: move httpin_test package to httpin 2021-04-29 11:07:43 +08:00
httpin_test.go refactor: add internal package to reduce public API surface 2021-05-08 15:27:37 +08:00
httpin.go refactor: add internal package to reduce public API surface 2021-05-08 15:27:37 +08:00
LICENSE Initial commit 2021-04-13 10:15:37 +08:00
Makefile refactor: update ci scripts 2021-04-23 11:26:31 +08:00
README.md chore(docs): update README 2021-05-08 16:33:23 +08:00
required.go refactor: add internal package to reduce public API surface 2021-05-08 15:27:37 +08:00
resolver_test.go feat: implement form and header extractors in directives 2021-04-30 19:10:07 +08:00
resolver.go refactor: add internal package to reduce public API surface 2021-05-08 15:27:37 +08:00

httpin

codecov

HTTP Input for Go - Decode an HTTP request into a custom struct

Define your input struct and then fetch your data!

Quick View

BEFORE (use net/http) AFTER (use httpin)
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.
}
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) {
	interfaceInput, err := httpin.New(ListUsersInput{}).Decode(r)
	if err != nil {
		// err can be *httpin.InvalidField
		return
	}

	input := interfaceInput.(*ListUsersInput)
	// do sth.
}

Features

  • Builtin directive form to decode a field from HTTP query, i.e. http.Request.Form
  • Builtin directive header to decode a field from HTTP headers, e.g. http.Request.Header
  • Builtin decoders used by form and header 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
  • Decode a field from multiple sources, e.g. both query and headers
  • Register or replace decoders for both builtin basic types and custom types
  • Define input struct with embedded struct fields
  • Builtin directive required to tag a field as required
  • Builtin encoders for basic types
  • Register or replace encoders for both builtin basic types and custom types
  • Register custom directive executors to extend the field resolving abilities, see directive required as an example and think about implementing your own directives like trim, to_lowercase, base58_to_int, etc.

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"`
}

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
}

Advanced - Use Middleware

First, set up the middleware for your handlers. We recommend using alice to chain your HTTP middleware functions.

func init() {
	mux.Handle("/users", alice.New(
		httpin.NewInput(ListUsersInput{}),
	).ThenFunc(ListUsers)).Methods("GET")
}

Second, fetch your input with only one line of code.

func ListUsers(rw http.ResponseWriter, r *http.Request) {
	input := r.Context().Value(httpin.Input).(*UserQuery)
	// do sth.
}