tonic

package
v0.10.2 Latest Latest
Warning

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

Go to latest
Published: Jun 24, 2020 License: MIT Imports: 19 Imported by: 0

README

tonic lets you write simpler gin handlers.
The way it works is that it generates wrapping gin-compatible handlers,
that do all the repetitive work and wrap the call to your simple tonic handler.

Package tonic handles path/query/body parameter binding in a single consolidated input object
which allows you to remove all the boilerplate code that retrieves and tests the presence
of various parameters.

Here is an example input object.

    type MyInput struct {
        Foo int    `path:"foo"`
        Bar string `query:"bar" default:"foobar"`
        Baz string `json:"baz"`
    }

Output objects can be of any type, and will be marshaled to JSON.

Input validation is performed after binding into the object using the validator library
(https://github.com/go-playground/validator). You can use the tag 'validate' on your object
definition to perform validation inside the tonic handler phase.

    type MyInput struct {
        Foo int    `path:"foo" validate:"required,gt=10"`
        Bar string `query:"bar" default:"foobar" validate:"nefield=Baz"`
        Baz string `json:"baz" validate:"required,email"`
    }

enum input validation is also implemented natively by tonic, and can check that the provided input
value corresponds to one of the expected enum values.

    type MyInput struct {
        Bar string `query:"bar" enum:"foo,buz,biz"`
    }


The handler can return an error, which will be returned to the caller.

Here is a basic application that greets a user on http://localhost:8080/hello/me

    import (
         "errors"
         "fmt"

         "github.com/gin-gonic/gin"
         "github.com/alexisvisco/gadgeto/tonic"
    )

    type GreetUserInput struct {
        Name string `path:"name" validate:"required,gt=3" description:"User name"`
    }

    type GreetUserOutput struct {
        Message string `json:"message"`
    }

    func GreetUser(c *gin.Context, in *GreetUserInput) (*GreetUserOutput, error) {
        if in.Name == "satan" {
            return nil, errors.New("go to hell")
        }
        return &GreetUserOutput{Message: fmt.Sprintf("Hello %s!", in.Name)}, nil
    }

    func main() {
        r := gin.Default()
        r.GET("/hello/:name", tonic.Handler(GreetUser, 200))
        r.Run(":8080")
    }


If needed, you can also override different parts of the logic via certain available hooks in tonic:
    - binding
    - error handling
    - render

We provide defaults for these (bind from JSON, render into JSON, error = http status 400).
You will probably want to customize the error hook, to produce finer grained error status codes.

The role of this error hook is to inspect the returned error object and deduce the http specifics from it.
We provide a ready-to-use error hook that depends on the juju/errors package (richer errors):
    https://github.com/alexisvisco/gadgeto/tree/master/tonic/utils/jujerr

Example of the same application as before, using juju errors:

    import (
         "fmt"

         "github.com/gin-gonic/gin"
         "github.com/juju/errors"
         "github.com/alexisvisco/gadgeto/tonic"
         "github.com/alexisvisco/gadgeto/tonic/utils/jujerr"
    )

    type GreetUserInput struct {
        Name string `path:"name" description:"User name" validate="required"`
    }

    type GreetUserOutput struct {
        Message string `json:"message"`
    }

    func GreetUser(c *gin.Context, in *GreetUserInput) (*GreetUserOutput, error) {
        if in.Name == "satan" {
            return nil, errors.NewForbidden(nil, "go to hell")
        }
        return &GreetUserOutput{Message: fmt.Sprintf("Hello %s!", in.Name)}, nil
    }

    func main() {
        tonic.SetErrorHook(jujerr.ErrHook)
        r := gin.Default()
        r.GET("/hello/:name", tonic.Handler(GreetUser, 200))
        r.Run(":8080")
    }


You can also easily serve auto-generated (using tonic data) swagger documentation:

    import (
         "fmt"

         "github.com/gin-gonic/gin"
         "github.com/juju/errors"
         "github.com/alexisvisco/gadgeto/tonic"
         "github.com/alexisvisco/gadgeto/tonic/utils/jujerr"
         "github.com/alexisvisco/gadgeto/tonic/utils/swag"
    )

    func main() {
        tonic.SetErrorHook(jujerr.ErrHook)
        r := gin.Default()
        r.GET("/hello/:name", tonic.Handler(GreetUser, 200))
        r.GET("/swagger.json", swag.Swagger(r, "MyAPI", swag.Version("v1.0"), swag.BasePath("/foo/bar")))
        r.Run(":8080")
    }

Documentation

Index

Constants

View Source
const (
	QueryTag      = "query"
	PathTag       = "path"
	HeaderTag     = "header"
	EnumTag       = "enum"
	RequiredTag   = "required"
	DefaultTag    = "default"
	ValidationTag = "validate"
	ExplodeTag    = "explode"
)

Fields tags used by tonic.

View Source
const DefaultMaxBodyBytes = 256 * 1024

DefaultMaxBodyBytes is the maximum allowed size of a request body in bytes.

Variables

This section is empty.

Functions

func CustomValidatorFunc

func CustomValidatorFunc(f func(v *validator.Validate) error) error

func DefaultErrorHook

func DefaultErrorHook(c *gin.Context, e error) (int, interface{})

DefaultErrorHook is the default error hook. It returns a StatusBadRequest with a payload containing the error message.

func DefaultExecHook

func DefaultExecHook(c *gin.Context, h gin.HandlerFunc, fname string)

DefaultExecHook is the default exec hook. It simply executes the wrapping gin-handler with the given context.

func DefaultRenderHook

func DefaultRenderHook(c *gin.Context, statusCode int, payload interface{})

DefaultRenderHook is the default render hook. It marshals the payload to JSON, or returns an empty body if the payload is nil. If Gin is running in debug mode, the marshalled JSON is indented.

func Deprecated

func Deprecated(b bool) func(*Route)

Deprecated set the deprecated flag of a route.

func Description

func Description(s string) func(*Route)

Description set the description of a route.

func GetRoutes

func GetRoutes() map[string]*Route

GetRoutes returns the routes handled by a tonic-enabled handler.

func Handler

func Handler(h interface{}, status int, options ...func(*Route)) gin.HandlerFunc

Handler returns a Gin HandlerFunc that wraps the handler passed in parameters. The handler may use the following signature:

func(*gin.Context, [input object ptr]) ([output object], error)

Input and output objects are both optional. As such, the minimal accepted signature is:

func(*gin.Context) error

The wrapping gin-handler will bind the parameters from the query-string, path, body and headers, and handle the errors.

Handler will panic if the tonic handler or its input/output values are of incompatible type.

func ListenAndServe

func ListenAndServe(handler http.Handler, errorHandler func(error), opt ...ListenOptFunc)

func MediaType

func MediaType() string

MediaType returns the current media type (MIME) used by the actual render hook.

func ParseTagKey

func ParseTagKey(tag string) (string, error)

ParseTagKey parses the given struct tag key and return the name of the field

func SetBindHook

func SetBindHook(bh BindHook)

SetBindHook sets the given hook as the default binding hook.

func SetErrorHook

func SetErrorHook(eh ErrorHook)

SetErrorHook sets the given hook as the default error handling hook.

func SetExecHook

func SetExecHook(eh ExecHook)

SetExecHook sets the given hook as the default execution hook.

func SetRenderHook

func SetRenderHook(rh RenderHook, mt string)

SetRenderHook sets the given hook as the default rendering hook. The media type is used to generate the OpenAPI specification.

func Summary

func Summary(s string) func(*Route)

Summary set the summary of a route.

func Tags

func Tags(tags []string) func(*Route)

Tags sets the tags of a route.

Types

type BindError

type BindError struct {
	// contains filtered or unexported fields
}

BindError is an error type returned when tonic fails to bind parameters, to differentiate from errors returned by the handlers.

func (BindError) Error

func (be BindError) Error() string

Error implements the builtin error interface for BindError.

func (BindError) ValidationErrors

func (be BindError) ValidationErrors() validator.ValidationErrors

ValidationErrors returns the errors from the validate process.

type BindHook

type BindHook func(*gin.Context, interface{}) error

BindHook is the hook called by the wrapping gin-handler when binding an incoming request to the tonic-handler's input object.

DefaultBindingHook is the default binding hook. It uses Gin JSON binding to bind the body parameters of the request to the input object of the handler. Ir teturns an error if Gin binding fails.

func DefaultBindingHookMaxBodyBytes

func DefaultBindingHookMaxBodyBytes(maxBodyBytes int64) BindHook

DefaultBindingHookMaxBodyBytes returns a BindHook with the default logic, with configurable MaxBodyBytes.

func GetBindHook

func GetBindHook() BindHook

GetBindHook returns the current bind hook.

type ErrorHook

type ErrorHook func(*gin.Context, error) (int, interface{})

ErrorHook lets you interpret errors returned by your handlers. After analysis, the hook should return a suitable http status code and and error payload. This lets you deeply inspect custom error types.

func GetErrorHook

func GetErrorHook() ErrorHook

GetErrorHook returns the current error hook.

type ExecHook

type ExecHook func(*gin.Context, gin.HandlerFunc, string)

An ExecHook is the func called to handle a request. The default ExecHook simply calle the wrapping gin-handler with the gin context.

func GetExecHook

func GetExecHook() ExecHook

GetExecHook returns the current execution hook.

type ListenOpt

type ListenOpt struct {
	Server          *http.Server
	Signals         []os.Signal
	ShutdownTimeout time.Duration
}

type ListenOptFunc

type ListenOptFunc func(*ListenOpt) error

func CatchSignals

func CatchSignals(sig ...os.Signal) ListenOptFunc

func KeepAliveTimeout

func KeepAliveTimeout(t time.Duration) ListenOptFunc

func ListenAddr

func ListenAddr(addr string) ListenOptFunc

func ReadHeaderTimeout

func ReadHeaderTimeout(t time.Duration) ListenOptFunc

func ReadTimeout

func ReadTimeout(t time.Duration) ListenOptFunc

func ShutdownTimeout

func ShutdownTimeout(t time.Duration) ListenOptFunc

func WriteTimeout

func WriteTimeout(t time.Duration) ListenOptFunc

type RenderHook

type RenderHook func(*gin.Context, int, interface{})

RenderHook is the last hook called by the wrapping gin-handler before returning. It takes the Gin context, the HTTP status code and the response payload as parameters. Its role is to render the payload to the client to the proper format.

func GetRenderHook

func GetRenderHook() RenderHook

GetRenderHook returns the current render hook.

type Route

type Route struct {
	gin.RouteInfo
	// contains filtered or unexported fields
}

A Route contains information about a tonic-enabled route.

func GetRouteByHandler

func GetRouteByHandler(h gin.HandlerFunc) (*Route, error)

GetRouteByHandler returns the route informations of the given wrapped handler.

func (*Route) GetDefaultStatusCode

func (r *Route) GetDefaultStatusCode() int

GetDefaultStatusCode returns the default status code of the route.

func (*Route) GetDeprecated

func (r *Route) GetDeprecated() bool

GetDeprecated returns the deprecated flag of the route.

func (*Route) GetDescription

func (r *Route) GetDescription() string

GetDescription returns the description of the route.

func (*Route) GetHandler

func (r *Route) GetHandler() reflect.Value

GetHandler returns the handler of the route.

func (*Route) GetPath

func (r *Route) GetPath() string

GetPath returns the path of the route.

func (*Route) GetSummary

func (r *Route) GetSummary() string

GetSummary returns the summary of the route.

func (*Route) GetTags

func (r *Route) GetTags() []string

GetTags generates a list of tags for the swagger spec from one route definition. It uses the first chunk of the path of the route as the tag (for example, in /foo/bar it will return the "foo" tag), unless specific tags have been defined with tonic.Tags

func (*Route) GetVerb

func (r *Route) GetVerb() string

GetVerb returns the HTTP verb of the route.

func (*Route) HandlerName

func (r *Route) HandlerName() string

HandlerName returns the name of the route handler.

func (*Route) HandlerNameWithPackage

func (r *Route) HandlerNameWithPackage() string

HandlerNameWithPackage returns the full name of the rout handler with its package path.

func (*Route) InputType

func (r *Route) InputType() reflect.Type

InputType returns the input type of the handler. If the type is a pointer to a concrete type, it is dereferenced.

func (*Route) OutputType

func (r *Route) OutputType() reflect.Type

OutputType returns the output type of the handler. If the type is a pointer to a concrete type, it is dereferenced.

Directories

Path Synopsis
utils
eis

Jump to

Keyboard shortcuts

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