bind

package
v0.0.0-...-f1f797b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 6, 2024 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package bind contains model binding features to be used along *http.Request.

Binding functions

All the functions of this package are binding functions, that parse the string data from the request and binds the result to a Go struct. Supported functions are:

  • [URIParameters] - one or more URL parameters;
  • [URIParameter] - one URL parameter;
  • Body - body or forms (of any kind);
  • Query - query parameters or GET forms;
  • Header - one or more header fields;
  • HeaderField - one header field;
  • Request - optimized combination of [URIParameters], Body, Query and Header. Preferred for reading from multiple inputs.

If any of these functions fails to parse the request (for example, they couldn't bind a non-numeric string into an integer field), they return *Error, that contains a user-friendly message and can be used in the response as is.

Receiving files

Body and Request support uploading of files from multipart form requests, with the only requirements being using the appropriate "file" tag and making targeted fields of type *mime/multipart.FileHeader (or its slice variant). Check the package-level example.

Validations

The bind package is integrated with the github.com/libchaos/litchi/validate package. If a struct passed as type parameter for Request, [URIParameters], Body, Query or Header implements the github.com/libchaos/litchi/validate.Validatable interface with a pointer receiver, these binding functions validate the result and can return a validation error with a user-friendly message.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrUnsupportedContentType = errors.New("unsupported Content-Type")

Functions

func Body

func Body[T any](r *http.Request) (T, error)

Body binds the request's body into the fields of a struct of type T.

It checks the Content-Type header to select an appropriated parsing method:

  • "application/json" for JSON parsing
  • "application/xml" or "text/xml" for XML parsing
  • "application/x-yaml" for YAML parsing
  • "application/x-www-form-urlencoded" or "multipart/form-data" for form parsing

Tags from encoding packages, such as "json", "xml" and "yaml" tags, can be used appropriately. For form parsing, use the tag "form".

For files inside multipart forms, use the tag "file". Target fields should also be of type *mime/multipart.FileHeader or []*mime/multipart.FileHeader. The maximum number of bytes stored in memory is 32MB, while the rest is stored in temporary files.

If the Content-Type header is not set, Body defaults to JSON parsing. If it is not supported, it returns ErrUnsupportedContentType.

If *T implements validate.Validatable (with a pointer receiver), Body calls validate.Fields on the result and can return validate.Error.

If T is not a struct type, Body panics.

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"

	"github.com/libchaos/litchi/bind"
)

func main() {
	req := httptest.NewRequest(http.MethodPost, "/books", strings.NewReader(`
		{"name": "Percy Jackson", "publishYear": 2009}
	`))

	r := req

	type RequestBody struct {
		Name        string `json:"name"`
		PublishYear int    `json:"publishYear"`
	}

	body, err := bind.Body[RequestBody](r)
	if err == nil {
		fmt.Println(body.Name)
		fmt.Println(body.PublishYear)
	}

}
Output:

Percy Jackson
2009
Example (Form)
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/libchaos/litchi/bind"
)

func main() {
	req := httptest.NewRequest(http.MethodGet, "/books", nil)
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.URL.RawQuery = "publishYear=2009&name=Percy%20Jackson"

	r := req

	type RequestBody struct {
		Name        string `form:"name"`
		PublishYear int    `form:"publishYear"`
	}

	body, err := bind.Body[RequestBody](r)
	if err == nil {
		fmt.Println(body.Name)
		fmt.Println(body.PublishYear)
	}

}
Output:

Percy Jackson
2009
func Header[T any](r *http.Request) (T, error)

Header binds the request's header into the fields of a struct of type T. Targeted fields should be exported and annotated with the tag "header" (case-insensitive). Otherwise, they are ignored.

If any field couldn't be bound, Header returns Error.

If *T implements validate.Validatable (with a pointer receiver), Header calls validate.Fields on the result and can return validate.Error.

If T is not a struct type, Header panics.

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/libchaos/litchi/bind"
)

func main() {
	req := httptest.NewRequest(http.MethodGet, "/books", nil)
	req.Header.Add("Content-Length", "150")
	req.Header.Add("Authorization", "Bearer uPSsoa65gqkFv2Z6sZ3rZCZwnCjzaXe8TNdk0bJCFFJGrH6wmnzyK4evHBtTuvVH")

	r := req
	type Header struct {
		ContentLength uint   `header:"Content-Length"`
		Authorization string `header:"Authorization"`
	}

	h, err := bind.Header[Header](r)
	if err == nil {
		fmt.Println(h.ContentLength)
		fmt.Println(h.Authorization)
	}

}
Output:

150
Bearer uPSsoa65gqkFv2Z6sZ3rZCZwnCjzaXe8TNdk0bJCFFJGrH6wmnzyK4evHBtTuvVH

func HeaderField

func HeaderField[T primitiveType | time.Time](r *http.Request, header string) (T, error)

HeaderField binds a field from the request's header into a value of type T. T can be either a primitive type or a time.Time.

HeaderField consider header as case-insensitive.

If the value can't be bound into T, HeaderField returns Error.

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/libchaos/litchi/bind"
)

func main() {
	req := httptest.NewRequest(http.MethodGet, "/books", nil)
	req.Header.Add("Content-Length", "150")
	req.Header.Add("Authorization", "Bearer uPSsoa65gqkFv2Z6sZ3rZCZwnCjzaXe8TNdk0bJCFFJGrH6wmnzyK4evHBtTuvVH")

	r := req

	contentLength, err := bind.HeaderField[int](r, "Content-Length")
	if err == nil {
		fmt.Println(contentLength)
	}

	authorization, err := bind.HeaderField[string](r, "authorization") // case-insensitive
	if err == nil {
		fmt.Println(authorization)
	}

}
Output:

150
Bearer uPSsoa65gqkFv2Z6sZ3rZCZwnCjzaXe8TNdk0bJCFFJGrH6wmnzyK4evHBtTuvVH

func Query

func Query[T any](r *http.Request) (T, error)

Query binds the request's query parameters into the fields of a struct of type T. Targeted fields should be exported and annotated with the tag "query". Otherwise, they are ignored.

If a field can't be bound, Query returns Error.

If *T implements validate.Validatable (with a pointer receiver), Query calls validate.Fields on the result and can return validate.Error.

If T is not a struct type, Query panics.

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/libchaos/litchi/bind"
)

func main() {
	req := httptest.NewRequest(http.MethodGet, "/books", nil)
	req.URL.RawQuery = "publish_year=2009&name=Percy%20Jackson"

	r := req
	type BookQuery struct {
		PublishYear uint   `query:"publish_year"`
		Name        string `query:"name"`
	}

	query, err := bind.Query[BookQuery](r)
	if err == nil {
		fmt.Println(query.PublishYear, query.Name)
	}

}
Output:

2009 Percy Jackson
Example (Form)
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/libchaos/litchi/bind"
)

func main() {
	req := httptest.NewRequest(http.MethodGet, "/books", nil)
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.URL.RawQuery = "publish_year=2009&name=Percy%20Jackson"

	r := req
	type BookQuery struct {
		PublishYear uint   `query:"publish_year"`
		Name        string `query:"name"`
	}

	query, err := bind.Query[BookQuery](r)
	if err == nil {
		fmt.Println(query.PublishYear, query.Name)
	}

}
Output:

2009 Percy Jackson

func Request

func Request[T any](r *http.Request) (T, error)

Request binds the request's body, query, header and URI parameters into the fields of a struct of type T. Targeted fields should be exported and annotated with corresponding binding tags. Otherwise, they are ignored.

It's an optimized combination of the binding functions Body, Query, Header and [URIParameters], suitable when you need to read from multiple inputs of the request.

If a field can't be bound, Request returns Error.

If *T implements validate.Validatable (with a pointer receiver), Request calls validate.Fields on the result and can return validate.Error.

If T is not a struct type, Request panics.

Types

type Error

type Error struct {
	// Incoming value.
	Value string
	// Target of the binding.
	Target reflect.Type
	// The actual error.
	Err error
}

Error is returned when a binding to a target value fails.

func (Error) Error

func (e Error) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL