dispatch

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

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

Go to latest
Published: Mar 20, 2020 License: MIT Imports: 10 Imported by: 0

README

github.com/daniel-lawrence/dispatch

Dispatch is a not-too-complicated framework meant for creating super quick and easy JSON APIs. Error handling, CORS, JSON parsing, and more are all handled out of the box—even user management and authentication!

Basic Usage

This program creates and serves an API with a single endpoint. The endpoint simply returns a string composed with the {name} path variable.

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/daniel-lawrence/dispatch"
)

func rootHandler(ctx *dispatch.Context) string {
	return fmt.Sprintf("Hello, %s!", ctx.PathVars["name"])
}

func main() {
	api := &dispatch.API{}
	api.AddEndpoint("GET/{name}", rootHandler)
	http.HandleFunc("/", api.GetHandler())
	log.Fatal(http.ListenAndServe(":8000", nil))
}

API Paths

Paths are expressed as a simple string, in the form:

METHOD/path/{pathvar}

Any path variables in curly braces will be automatically parsed and provided to handler functions in the dispatch.Context.PathVars map. Any path elements not in curly braces are treated as literals, and must be matched for the handler to be called.

API Endpoints

Endpoints return JSON when used, but the handler functions themselves can accept and return any time, with certain restrictions.

A handler function's input signature can be any of these four types:

  • (none)
  • (*dispatch.Context)
  • (<AnyType>)
  • (<AnyType>, *dispatch.Context) (order does not matter)

If the handler accepts an input type other than *dispatch.Context, it can be anything—a string, a struct, or whatever else. Dispatch will automagically marshal any incoming JSON into your type for you.

A handler function's output signature is slightly more restricted:

  • (none)
  • (error)
  • (<AnyType>)
  • (<AnyType>, error) (order does matter)

If your function returns an error, the handler provided by the api package will automatically return an HTTP error. dispatch.ErrorNotFound and dispatch.ErrorBadRequest errors will also be accompianied by correct HTTP status codes. Otherwise, dispatch will simply return status 500 and the text of your error.

Middleware

The api.AddEndpoint method also allows adding middleware hooks. These hooks are functions which will be called before the endpoint handler is called, and can choose to modify the method, path, context, or input of the endpoint before it is passed along. If the hook returns an error, execution of the endpoint will halt. This is useful for things like authentication checks, which must happen before the function is triggered, and must be able to return early if a call isn't authorized.

Known Issues/Disclaimer

User management and authentication is very simplistic and untested. This shouldn't be used in any sort of production environment, and shouldn't be considered secure. Additionally, access control headers allow a hardcoded value of * for the origin, and only specific content types.

Dispatch was created for a specific purpose, so there are many parts of the library that are too inflexible for many use cases.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrorBadRequest = errors.New("Bad request")

ErrorBadRequest represents an error from a malformed request.

View Source
var ErrorInternal = errors.New("Internal error")

ErrorInternal represents some unexpected internal error.

View Source
var ErrorNotFound = errors.New("Path not found")

ErrorNotFound represents a 404 error.

Functions

This section is empty.

Types

type API

type API struct {
	Endpoints []*Endpoint
}

API is an object that holds all API methods and can dispatch them.

func (*API) AddEndpoint

func (api *API) AddEndpoint(path string, handler interface{}, hooks ...MiddlewareHook)

AddEndpoint registers an endpoint with this API. It also allows adding middleware hooks to the endpoint.

func (*API) Call

func (api *API) Call(method, path string, ctx *Context, input json.RawMessage) (out interface{}, err error)

Call sends the input to the endpoint and returns the result.

func (*API) GetHandler

func (api *API) GetHandler() func(http.ResponseWriter, *http.Request)

GetHandler returns a handler function suitable for use in http.HandleFunc. For example:

http.HandleFunc("/", api.GetHandler())
log.Fatal(http.ListenAndServe(":8000", nil))

The provided handler takes care of access control headers, CORS requests, JSON marshalling, and error handling.

func (*API) MatchEndpoint

func (api *API) MatchEndpoint(method, path string) (*Endpoint, PathVars)

MatchEndpoint matches a request to an endpoint, creating a map of path variables in the process.

type APIPath

type APIPath struct {
	PathParts []string
	Method    string
}

An APIPath represents a specified path and method, such as GET/users/{uuid}.

func NewAPIPath

func NewAPIPath(path string) (*APIPath, error)

NewAPIPath creates an APIPath object from a path string, in the format GET/users/{uuid}.

func (*APIPath) Match

func (a *APIPath) Match(method, path string) (pathVars PathVars, ok bool)

Match tests an APIPath against a path string, and returns a map of path variables and a boolean representing whether it was a match.

type Context

type Context struct {
	// Request is the original http request.
	Request *http.Request
	// PathVars is the map of path variable names to values.
	PathVars PathVars
	Claims   *auth.Claims
}

Context represents data about the endpoint call, such as path variables, the calling user, and so on.

type Endpoint

type Endpoint struct {

	// Path is the API path string that will be exposed as an API endpoint. Must
	// be unique.
	//
	// The format of Path is METHOD/path/{pathvar}. Any path variables in curly
	// brace notation will be parsed during API.Call and passed to Handler as
	// a Context struct value.
	Path string

	// Handler must be a function that receives any single input variable, an
	// input variable of type Context, neither, or both. It can return one
	// output variable of any time, an error, neither, or both in the order
	// (output, error).
	//
	// The input value for Handler, if not Context, will automatically be
	// unmarshalled from the input to API.Call.
	Handler interface{}

	// PreRequestHook is a middleware hook that runs before the handler. If the
	// hook returns an error, that error will be returned and the handler will
	// not be called.
	PreRequestHook MiddlewareHook
	// contains filtered or unexported fields
}

An Endpoint represents an API procedure.

type EndpointInput

type EndpointInput struct {
	Method string
	Path   string
	Ctx    *Context
	Input  json.RawMessage
}

EndpointInput represents the input to an endpoint call. These inputs can be modified by middleware hooks.

func AuthorizerHook

func AuthorizerHook(input *EndpointInput, token *auth.TokenSigner) (*EndpointInput, error)

AuthorizerHook is a middleware hook that populates the context's Claims object with data from the request's authorization token. If there is no authorization token, or the token is invalid, it returns an error.

This hook effectively acts as a requirement that the authorization token is correct.

type MiddlewareHook

type MiddlewareHook func(*EndpointInput) (*EndpointInput, error)

MiddlewareHook is a function type that is called for each request.

These hooks are functions which will be called before the endpoint handler is called, and can choose to modify the method, path, context, or input of the endpoint before it is passed along. If the hook returns an error, execution of the endpoint will halt. This is useful for things like authentication checks, which must happen before the function is triggered, and must be able to return early if a call isn't authorized.

type PathVars

type PathVars map[string]string

PathVars is an alias for map[string]string, used for captured path variables.

Directories

Path Synopsis
Package auth provides utility functions for password authentication and JWT access control.
Package auth provides utility functions for password authentication and JWT access control.

Jump to

Keyboard shortcuts

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