1
0
mirror of https://github.com/ggicci/httpin.git synced 2024-11-28 08:49:05 +02:00
httpin/README.md
2021-05-08 16:42:32 +08:00

3.3 KiB

httpin

codecov

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)
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;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
}

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.
}