eprouter

package module
v0.10.1 Latest Latest
Warning

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

Go to latest
Published: Mar 24, 2015 License: MIT Imports: 15 Imported by: 0

README

eprouter

Designed for quickly making REST APIs, w/ easy config and just enough magic to keep you productive, written in Go Spiritual Successor to amattn/grunway

Installation

go get github.com/collectivehealth/eprouter

Usage

eprouter is built around 2 central concepts:

  1. Defining high-level routes
  2. Quickly implementing handlers

Under the hood, routes map to endpoints which call handlers. refelection is used during startup to build an internal routing map, but not during routing of requests.

Defining High-level Routes

Looks like this:

routerPtr := eprouter.NewRouter()
routerPtr.BasePath = "/api/"
routerPtr.RegisterEntity("book", &BookController{})
routerPtr.RegisterEntity("author", &AuthorController{})
return routerPtr

You then start the server like so:

host := ":8090"
err := eprouter.Start(routerPtr, host)
if err != nil {
	log.Fatalln(err)
}
Quickly implementing handlers

Here is where we use a bit of reflection. Instead of defining routes and hooking up controllers, we just immplement handlers.

Like this:

func (bc *BookController) GetHandlerV1(ctx *eprouter.Context) eprouter.RouteHandlerResult {
   //...
}
func (bc *BookController) GetHandlerV2(ctx *eprouter.Context) eprouter.RouteHandlerResult {
	//...
}
func (bc *BookController) GetHandlerV1Popular(ctx *eprouter.Context) eprouter.RouteHandlerResult {
    //...
}

The basic function signature is a RouteHandler:

type RouteHandler func(*Context) RouteHandlerResult

99% of the time you either return a PayloadsMap or a RouteError. If you need special control of the response, a CustomRouteResponse is a special handler with more access to the output stream.

The format of the Handlers works like this:

<Method>HandlerV<version><Action>

Which corresponts to http endpoints like this:

<Method> http://host/<prefix>/v<version>/<Entity>/<optionalID>/<Action>
Auth

Auth is a bit special that it has its own dedicated handler prefix:

func (wc *WidgetController) AuthGetHandlerV1(ctx *eprouter.Context) eprouter.RouteHandlerResult {
   //...
}

There is a example package for handling auth at http://github.com/amattn/grwacct

Documentation

Index

Constants

View Source
const (
	HttpHeaderContentType     = "Content-Type"
	HttpHeaderContentTypeJSON = "application/json"
)
View Source
const (
	MAGIC_AUTH_REQUIRED_PREFIX  = "Auth"
	MAGIC_HANDLER_KEYWORD       = "Handler"
	MAGIC_GET_HANDLER_PREFIX    = "GetHandler"    // CRUD: read
	MAGIC_POST_HANDLER_PREFIX   = "PostHandler"   // CRUD: create
	MAGIC_PUT_HANDLER_PREFIX    = "PutHandler"    // CRUD: update (the whole thing)
	MAGIC_PATCH_HANDLER_PREFIX  = "PatchHandler"  // CRUD: update (just a field or two)
	MAGIC_DELETE_HANDLER_PREFIX = "DeleteHandler" // CRUD: delete (duh)
	MAGIC_HEAD_HANDLER_PREFIX   = "HeadHandler"   // usually when you just want to check Etags or something.
)
View Source
const (
	NotFoundPrefix                            = "404 Not Found"
	NotFoundErrorNumber                       = 4040000404
	BadRequestPrefix                          = "400 Bad Request"
	BadRequestErrorNumber                     = 4000000000
	BadRequestSyntaxErrorPrefix               = BadRequestPrefix + ": Syntax Error"
	BadRequestSyntaxErrorErrorNumber          = 4000000001
	BadRequestMissingPrimaryKeyErrorNumber    = 4000000002
	BadRequestExtraneousPrimaryKeyErrorNumber = 4000000003

	InternalServerErrorPrefix = "500 Internal Server Error"
)
View Source
const ROUTE_MAP_SEPARATOR = "-{&|!?}-"

RouteMap helpers

View Source
const VERSION_BIT_DEPTH = 16

Variables

This section is empty.

Functions

func BuildNumber

func BuildNumber() int64

func MarshallPayloadWrapper

func MarshallPayloadWrapper(pw *PayloadWrapper) ([]byte, error)

func Start

func Start(routerPtr *Router, host string) error

func ValidateEntityName

func ValidateEntityName(name string) (isValid bool, reason string)

func ValidateHandlerName

func ValidateHandlerName(handler interface{}) (isValid bool, reason string)

func Version

func Version() string

Types

type AuthHandler

type AuthHandler interface {
	PerformAuth(routePtr *Route, ctx *Context) (authenticationWasSucessful bool, failureToAuthErrorNum int, failureToAuthErrorMessage string)
}

type CommonLogger

type CommonLogger struct {
}

func (*CommonLogger) Process

func (logger *CommonLogger) Process(ctx *Context) (terminateEarly bool, derr *deeperror.DeepError)

type Context

type Context struct {
	Req      *http.Request // original http request
	Endpoint Endpoint      // parsed endpoint information

	StatusCode    int                    // The http status code written out. Only populated after a write.
	ContentLength int                    // The number of bytes written out. Only populated after a write.
	AuthInfo      map[string]interface{} // a k/v store of any info about the user which was gleaned during authentication
	// contains filtered or unexported fields
}

func (*Context) AddResponseHeader

func (ctx *Context) AddResponseHeader(key, value string)

func (*Context) DecodeResponseBodyOrSendError

func (ctx *Context) DecodeResponseBodyOrSendError(pc PayloadController, payloadReference interface{}) interface{}

func (*Context) DeleteResponseHeader

func (ctx *Context) DeleteResponseHeader(key string)

func (*Context) GetResponseHeader

func (ctx *Context) GetResponseHeader(key string) string

func (*Context) MakeRouteHandlerResultAlert

func (ctx *Context) MakeRouteHandlerResultAlert(code int, errNo int64, alert string) RouteHandlerResult

func (*Context) MakeRouteHandlerResultCustom

func (ctx *Context) MakeRouteHandlerResultCustom(crr CustomRouteResponse) RouteHandlerResult

func (*Context) MakeRouteHandlerResultDebugError

func (ctx *Context) MakeRouteHandlerResultDebugError(code int, errNo int64, errMsg string, debugNo int64, debugMsg string) RouteHandlerResult

func (*Context) MakeRouteHandlerResultError

func (ctx *Context) MakeRouteHandlerResultError(code int, errNo int64, errMsg string) RouteHandlerResult

func (*Context) MakeRouteHandlerResultErrorInfo

func (ctx *Context) MakeRouteHandlerResultErrorInfo(code int, errInfo ErrorInfo) RouteHandlerResult

func (*Context) MakeRouteHandlerResultGenericJSON

func (ctx *Context) MakeRouteHandlerResultGenericJSON(v interface{}) RouteHandlerResult

func (*Context) MakeRouteHandlerResultNotFound

func (ctx *Context) MakeRouteHandlerResultNotFound(errNo int64) RouteHandlerResult

func (*Context) MakeRouteHandlerResultOk

func (ctx *Context) MakeRouteHandlerResultOk() RouteHandlerResult

func (*Context) MakeRouteHandlerResultPayloads

func (ctx *Context) MakeRouteHandlerResultPayloads(payloads ...Payload) RouteHandlerResult

func (*Context) MakeRouteHandlerResultRawBytes

func (ctx *Context) MakeRouteHandlerResultRawBytes(statusCode int, rawBytes []byte, contentType string) RouteHandlerResult

func (*Context) MakeRouteHandlerResultStatusGenericJSON

func (ctx *Context) MakeRouteHandlerResultStatusGenericJSON(statusCode int, v interface{}) RouteHandlerResult

func (*Context) RequestBody

func (ctx *Context) RequestBody() ([]byte, error)

func (*Context) SendErrorInfoPayload

func (ctx *Context) SendErrorInfoPayload(code int, errInfo ErrorInfo)

func (*Context) SendSimpleAlertPayload

func (ctx *Context) SendSimpleAlertPayload(code int, errNo int64, errMsg, alert string)

Alert payloads are designed as a general notification service for clients (ie client must upgrade, server is in maint mode, etc.)

func (*Context) SendSimpleErrorPayload

func (ctx *Context) SendSimpleErrorPayload(code int, errNo int64, errorMsg string)

Error

func (*Context) SetResponseHeader

func (ctx *Context) SetResponseHeader(key, value string)

func (*Context) WrapAndSendPayload

func (ctx *Context) WrapAndSendPayload(payload Payload)

func (*Context) WrapAndSendPayloadsList

func (ctx *Context) WrapAndSendPayloadsList(payloadList []Payload)

for a slice of Entities

func (*Context) WrapAndSendPayloadsMap

func (ctx *Context) WrapAndSendPayloadsMap(payloads PayloadsMap)

for a map of slices of Entities

type CustomRouteResponse

type CustomRouteResponse func(*Context)

type Endpoint

type Endpoint struct {
	VersionStr string
	EntityName string
	PrimaryKey string
	Action     string
	Components []string
	Extras     []string
	// contains filtered or unexported fields
}

convenience struct holding all the stuff you usually want to know about an endpoint

func ParsePathForTesting

func ParsePathForTesting(urlPtr *url.URL, prefix string) (endpoint Endpoint, err error)

exported for other packages to be able to unit test.

func (*Endpoint) Version

func (e *Endpoint) Version() VersionUint

return a typed number, not a string cache value so we only do this once.

type ErrorInfo

type ErrorInfo struct {
	ErrorNumber  int64  `json:"errorNumber,omitempty"`  // will be 0 on successful responses, non-zero otherwise
	ErrorMessage string `json:"errorMessage,omitempty"` // end-user appropriate error message
	DebugNumber  int64  `json:"debugNumber,omitempty"`  // optional debug code
	DebugMessage string `json:"debugMessage,omitempty"` // optional debug message
}

type MiddlewareProcessor

type MiddlewareProcessor interface {
	Process(routePtr *Route, ctx *Context) (terminateEarly bool, derr *deeperror.DeepError)
}

type Payload

type Payload interface {
	PayloadType() string
}

If a payload implements this method, then the wrapper will autopopulate the PayloadType field

type PayloadController

type PayloadController interface {
}

type PayloadWrapper

type PayloadWrapper struct {
	Payloads PayloadsMap `json:",omitempty"` // key is type, value is list of payloads of that type

	ErrorInfo
	Alert string `json:",omitempty"` // used when the client end user needs to be alerted of something: (eg, maintenance mode, downtime, sercurity, required update, etc.)
}

This will typically be serialized into a JSON formatted string

func NewPayloadWrapper

func NewPayloadWrapper(payloadsList ...Payload) *PayloadWrapper

func UnmarshalPayloadWrapper

func UnmarshalPayloadWrapper(jsonBytes []byte, supportedPayloads ...Payload) (*PayloadWrapper, error)

type PayloadsMap

type PayloadsMap map[string][]Payload

ALWAYS a map of array of objects. key is type, value is list of payloads of that type Typically, these are arrays of objects designed to be deserialized into entity structs (eg []BookPayload, []AuthorPayload)

func MakePayloadMapFromPayloads

func MakePayloadMapFromPayloads(payloadsList ...Payload) PayloadsMap

type PostProcessor

type PostProcessor interface {
	Process(ctx *Context) (terminateEarly bool, derr *deeperror.DeepError)
}

type PreProcessor

type PreProcessor interface {
	Process(ctx *Context) (terminateEarly bool, derr *deeperror.DeepError)
}

type Route

type Route struct {
	RequiresAuth  bool
	Authenticator AuthHandler

	Method         string
	Path           string
	VersionStr     string
	EntityName     string
	Action         string
	Handler        RouteHandler
	HandlerName    string // not actually used except for logging and debugging
	ControllerName string // not actually used except for logging and debugging
}

type RouteError

type RouteError struct {
	ErrorLevel levels.ErrorLevel
	// contains filtered or unexported fields
}

func NewRouteError

func NewRouteError(code int, errInfo ErrorInfo) *RouteError

type RouteHandler

type RouteHandler func(*Context) RouteHandlerResult

func ValidateHandler

func ValidateHandler(unknownHandler interface{}) (isValid bool, reason string, handler RouteHandler)

type RouteHandlerResult

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

type Router

type Router struct {
	BasePath string

	PreProcessors        []PreProcessor
	MiddlewareProcessors []MiddlewareProcessor
	PostProcessors       []PostProcessor

	Controllers map[string]PayloadController // key is entity name
	RouteMap    map[string]*Route            // key is entity name
}

func NewRouter

func NewRouter() *Router

func (*Router) AddEntityRoute

func (router *Router) AddEntityRoute(entityName, controllerName, handlerName string, unknownhandler interface{}, authenticator AuthHandler)

func (*Router) AllRoutesCount

func (router *Router) AllRoutesCount() int

Convenience method

func (*Router) AllRoutesDescription

func (router *Router) AllRoutesDescription(addons ...string) []string

Basically just used for logging and debugging. the first addon is a prefix, all remaining addons are treated as suffixes and appended to the end

func (*Router) AllRoutesSummary

func (router *Router) AllRoutesSummary(addons ...string) string

Basically just used for logging and debugging. the first addon is a prefix, all remaining addons are treated as suffixes and appended to the end

func (*Router) LogAllRoutes

func (router *Router) LogAllRoutes(addons ...string)

func (*Router) RegisterEntity

func (router *Router) RegisterEntity(name string, payloadController PayloadController)

func (*Router) ServeHTTP

func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

type VersionUint

type VersionUint uint16

Jump to

Keyboard shortcuts

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