request

package module
v0.0.0-...-df7c1e8 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2022 License: MIT Imports: 9 Imported by: 0

README

request GoDoc GitHub Workflow Status GitHub

Package request implements simple decoding of http request - queries, headers and body - into golang struct for easier consumption, resulting in less code boilerplate.

godoc go.expect.digital/request

Documentation

Overview

Package request implements simple decoding of http request - url path, queries, headers and body - into golang struct for easier consumption, resulting in less code boilerplate.

Implementation is based on OpenAPI 3 specification https://swagger.io/docs/specification/about/.

func (r *http.Request, w *http.Response) {
	var req struct {
		// path - requires Decoder.Path.Get to get value of path parameter
		Id `path:"id"`

		// query params
		ExplodedIds []int `query:"id"`           // ?id=1&id=2&id=3
		ImplodedIds []int `query:"ids,imploded"` // ?ids=1,2,3
		Search string                            // ?search=foobar

		// body
		Client Client `body:"json"`
	}

	if err := request.Decode(r, &req); err != nil {
		// ...
	}
}

Index

Examples

Constants

View Source
const (
	QueryDelimiterPipe  = "|"
	QueryDelimiterSpace = " "
	QueryDelimiterComma = ","
)

List of supported delimiters.

View Source
const (
	QueryStyleForm  = "form"  // ?id=3,4,5
	QueryStyleSpace = "space" // ?id=3%204%205
	QueryStylePipe  = "pipe"  // ?id=3|4|5
	QueryStyleDeep  = "deep"  // ?id[role]=admin&id[firstName]=Alex
)

List of supported serialization styles.

Variables

This section is empty.

Functions

func Decode

func Decode(r *http.Request, i interface{}) error

Decode decodes http request into golang struct using defaults of OpenAPI 3 specification.

Example
package main

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

	"go.expect.digital/request"
)

func main() {
	r := httptest.NewRequest(
		http.MethodPost,
		"/?filterType=pending,approved&clientId=4&filterClientids=1|2|3",
		strings.NewReader(`{"id":1}`),
	)

	var req struct {
		// query params
		FilterType      []string `query:"filterType,imploded"`
		ClientId        int
		FilterClientIds []int `query:",pipe,imploded"`

		// body
		Client struct {
			Id int
		} `body:"json"`
	}

	_ = request.Decode(r, &req)

	fmt.Printf("%+v\n", req)
}
Output:

{FilterType:[pending approved] ClientId:4 FilterClientIds:[1 2 3] Client:{Id:1}}

Types

type Decoder

type Decoder struct {
	Path  PathConf
	Query QueryConf
}

func NewDecoder

func NewDecoder() Decoder

func (Decoder) Decode

func (d Decoder) Decode(r *http.Request, i interface{}) error

Decode decodes http request into golang struct.

Decoding of query params follows specification of https://swagger.io/docs/specification/serialization/#query.

// required - decoding returns error if query param is not present
var req struct {
	Name string `query:",required"`
}

// default - ?id=1&id=2&id=3
var req struct {
	Id []int // case insensitive match of field name and query parameter
}

// comma delimited - ?id=1,2,3
var req struct {
	Id []int  `query:",form"`      // implicitly imploded
	Ids []int `query:"id,imploded` // form by default
}

// pipe delimited - ?id=1|2|3
var req struct {
	Id []int `query:",pipe" // implicitly imploded
}

// space delimited - ?id=1%202%203
var req struct {
	Id []int `query:",space"` // implicitly imploded
}

// set different name - ?id=1,2,3
var req struct {
	FilterClientIds []int `query:"id,form"` // implicitly imploded
}

Use encoding.TextUnmarshaler to implement custom decoding.

Decoding of request headers is NOT yet implemented.

Decoding of request body is simple - it uses either json or xml unmarshaller:

type Entity struct {
	Id int
}

// If no field tag value specified, "accept" request header is used to determine decoding. Uses json by default.
var req struct {
	Entity `body:""`
}

// Always use json umarshalling, ignore "accept" request header:
var req struct {
	Entity `body:"json"`
}

// Always use xml unmarshalling, ignore "accept" request header:
var req struct {
	Entity `body:"xml"`
}
Example
package main

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

	"go.expect.digital/request"
)

func main() {
	r := httptest.NewRequest(http.MethodPost, "/?ids=1,2,3", nil)

	var req struct {
		Ids []int
	}

	dec := request.NewDecoder()
	// set query values imploded "?ids=1,2,3" by default
	dec.Query.Exploded = false

	_ = dec.Decode(r, &req)

	fmt.Printf("%+v\n", req)
}
Output:

{Ids:[1 2 3]}

type PathConf

type PathConf struct {
	// Get returns value of path parameter.
	//
	//	// chi
	//	PathConf{
	//		Get: chi.URLParam
	//	}
	Get func(r *http.Request, name string) string
}

type QueryConf

type QueryConf struct {
	// true - "?id=1&id=2&id=3", false - "?id=1,2,3"
	Exploded bool
	// one of QueryStyleForm, QueryStyleSpace, QueryStylePipe or QueryStyleDeep
	Style string
	// one of QueryDelimiterPipe, QueryDelimiterSpace, QueryDelimiterComma
	Delimiter string
}

Jump to

Keyboard shortcuts

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