Documentation ¶
Overview ¶
Package router provides a simple yet powerful URL router and HandlerFunc dispatcher for web apps.
Ideas considered (heavily borrowing from express/connect):
- registering handlers based on HTTP verb/path combos should be easy, as this is most often used
- split complex HandlerFuncs into multiple smaller one which can be shared
- mount generic HandlerFuncs to be executed on every path
- registering and accessing paths with params (like :userid) should be easy
- store data on a requestContext, so it can be passed to later HandlerFuncs
- set a generic errorHandlerFunc and stop executing later handerFuncs as soon as an error occurs
- set a generic pageNotFound HandlerFunc
- handlers are regular `http.HandlerFunc` to be compatible with go
Basic usage
// Create a new router appRouter := router.NewRouter() // Register a HandlerFunc for GET/"hello" paths appRouter.Get("/hello", func(res http.ResponseWriter, req *http.Request) { res.Write([]byte("hello")) }) // Use this router appRouter.Handle("/") // Listen for requests http.ListenAndServe(":3000", nil)
More advanced usage
func main() { appRouter := router.NewRouter() // `Mount` mounts a handler for all paths (starting with `/`) // Always mount generic HandlerFuncs first. appRouter.Mount("/", logger) // We can use multiple handleFuncs evaluated in order. // `:userid` specifies the param `userid` so it will match any string. appRouter.Get("/user/:userid/hello", loadUser, handleUser) appRouter.Handle("/") http.ListenAndServe(":3000", nil) } func logger(res http.ResponseWriter, req *http.Request) { // The fist HandlerFunc to be executed // record the time when the request started start := time.Now() // Grab the current context and call // cntxt.Next() to handle over control to the next HandlerFunc. // Simply don't call cntxt.Next() if you don't want to call the following // HandlerFunc's (for instance, for access control reasons). router.Context(req).Next(res, req) // We log once all other HandlerFuncs are done executing // so it needs to come after our call to cntxt.Next() fmt.Println(req.Method, req.URL.Path, time.Since(start)) } func loadUser(res http.ResponseWriter, req *http.Request) { cntxt := router.Context(req) user, err := getUserFromDB(cntxt.Params["userid"]) if err != nil { // Let the ErrorHandler generate the error response. // We stop executing the following handlers cntxt.Error(res, req, err.Error(), 500) return } // Store the value in request specific store _ = cntxt.Set("user", user) // Pass over control to next HandlerFunc cntxt.Next(res, req) } func handleUser(res http.ResponseWriter, req *http.Request) { cntxt := router.Context(req) // Get a value from the request specific store if user, ok := cntxt.Get("user"); ok { if str, ok := user.(string); ok { // As last handlers, we should generate a response greeting := "Hello " + str res.Write([]byte(greeting)) return } } res.Write([]byte("Who are you?")) // We dont use cntxt.Next() as there are no more // HandlerFuncs to call. However, stuff wont explode // if you call cntxt.Next()` by mistake. } // func getUserFromDB...
This will log for each request. On "/user/:userid/hello" matching paths, it loads a user and saves it to the requestContext store and handleUser generates the response. Note all handlers are regular http.HandlerFunc and use a `Context` to hand over control and data to the next HandlerFunc.
Index ¶
- type ErrorHandler
- type RequestContext
- func (cntxt *RequestContext) Delete(key interface{})
- func (cntxt *RequestContext) Error(res http.ResponseWriter, req *http.Request, err string, code int)
- func (cntxt *RequestContext) ForceSet(key, val interface{})
- func (cntxt *RequestContext) Get(key interface{}) (val interface{}, ok bool)
- func (cntxt *RequestContext) Next(res http.ResponseWriter, req *http.Request)
- func (cntxt *RequestContext) Set(key, val interface{}) bool
- type Router
- func (router *Router) Delete(path string, handlers ...http.HandlerFunc)
- func (router *Router) Get(path string, handlers ...http.HandlerFunc)
- func (router *Router) Handle(pattern string)
- func (router *Router) Head(path string, handlers ...http.HandlerFunc)
- func (router *Router) Mount(mountPath string, handler http.HandlerFunc)
- func (router *Router) Options(path string, handlers ...http.HandlerFunc)
- func (router *Router) Patch(path string, handlers ...http.HandlerFunc)
- func (router *Router) Post(path string, handlers ...http.HandlerFunc)
- func (router *Router) Put(path string, handlers ...http.HandlerFunc)
- func (router *Router) ServeHTTP(res http.ResponseWriter, req *http.Request)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ErrorHandler ¶
ErrorHandler interface to which an errorHandler needs to comply.
Used as a field in the router to override the default RrrorHandler implementation. Its responsibility is to generate the http Response when an error occurs. That is, when requestContext.Error() gets called.
type RequestContext ¶
RequestContext contains data related to the current request
func Context ¶
func Context(req *http.Request) *RequestContext
Context returns a pointer to the RequestContext for the current request.
func (*RequestContext) Delete ¶
func (cntxt *RequestContext) Delete(key interface{})
Delete removes a key/value pair from the store.
func (*RequestContext) Error ¶
func (cntxt *RequestContext) Error(res http.ResponseWriter, req *http.Request, err string, code int)
Error allows you to respond with an error message preventing the subsequent handlers from being executed.
Note: in case there exist previous requestHandlers and they have code after their next call, that code will execute. This allows loggers and such to finish what they started (though they can also use a defer for that).
func (*RequestContext) ForceSet ¶
func (cntxt *RequestContext) ForceSet(key, val interface{})
ForceSet saves a value for the current request. Unlike Set, it will happily override existing data.
func (*RequestContext) Get ¶
func (cntxt *RequestContext) Get(key interface{}) (val interface{}, ok bool)
Get fetches data from the store associated with the current request.
func (*RequestContext) Next ¶
func (cntxt *RequestContext) Next(res http.ResponseWriter, req *http.Request)
Next invokes the next HandleFunc in line registered to handle this request.
This is needed when multiple HandleFuncs are registered for a given path and allows the creation and use of `middleware`.
func (*RequestContext) Set ¶
func (cntxt *RequestContext) Set(key, val interface{}) bool
Set saves a value for the current request. The value will not be set if the key already exist.
type Router ¶
type Router struct { NotFoundHandler http.HandlerFunc // Specify a custom NotFoundHandler ErrorHandler ErrorHandler // Specify a custom ErrorHandler // contains filtered or unexported fields }
A Router to register paths and requestHandlers to.
Set a custom NotFoundHandler if you want to override go's default one.
There can be multiple per application, if so, don't forget to pass a different pattern to `router.Handle()`.
func NewRouter ¶
func NewRouter() (router *Router)
NewRouter creates a router and returns a pointer to it so you can start registering routes.
Don't forget to call `router.Handle(pattern)` to actually use the router.
func (*Router) Delete ¶
func (router *Router) Delete(path string, handlers ...http.HandlerFunc)
Delete registers a DELETE path to be handled. Multiple handlers can be passed and will be evaluated in order (after the more generic mounted HandlerFuncs).
func (*Router) Get ¶
func (router *Router) Get(path string, handlers ...http.HandlerFunc)
Get registers a GET path to be handled. Multiple handlers can be passed and will be evaluated in order (after the more generic mounted HandlerFuncs).
func (*Router) Handle ¶
Handle registers the router for the given pattern in the DefaultServeMux. The documentation for ServeMux explains how patterns are matched.
This delegates to `http.Handle()` internally.
Most of the times, you just want to do `router.Handle("/")`.
func (*Router) Head ¶
func (router *Router) Head(path string, handlers ...http.HandlerFunc)
Head registers an HEAD path to be handled. Multiple handlers can be passed and will be evaluated in order (after the more generic mounted HandlerFuncs).
func (*Router) Mount ¶
func (router *Router) Mount(mountPath string, handler http.HandlerFunc)
Mount mounts a requestHandler for a given mountPath. The requestHandler will be executed on all paths which start like the mountPath.
For example: mountPath "/" will execute the requestHandler for all requests (each one starts with "/"), contrary to "/api" will only execute the handler on paths starting with "/api" like "/api", "/api/2", "api/users/23"...
This allows for the use of general middleware, unlike more specific middleware handlers which are registered on specific paths.
Use mount if your requestHandlers needs to be invoked on all/most paths so you don't have to register it again and again when registering handlers.
The mountPath don't accept tokens (like :user) but can access the params on the context if the path on which it is fired contains those tokens.
func (*Router) Options ¶
func (router *Router) Options(path string, handlers ...http.HandlerFunc)
Options registers an OPTONS path to be handled. Multiple handlers can be passed and will be evaluated in order (after the more generic mounted HandlerFuncs).
func (*Router) Patch ¶
func (router *Router) Patch(path string, handlers ...http.HandlerFunc)
Patch registers a PATCH path to be handled. Multiple handlers can be passed and will be evaluated in order (after the more generic mounted HandlerFuncs).
func (*Router) Post ¶
func (router *Router) Post(path string, handlers ...http.HandlerFunc)
Post registers a POST path to be handled. Multiple handlers can be passed and will be evaluated in order (after the more generic mounted HandlerFuncs).