Documentation
¶
Overview ¶
Package router is a thin layer over net/http.ServeMux (Go 1.22+ patterns) adding route groups, named routes with URL generation, resource controllers and three middleware stacks:
- server middleware runs on every request, even when no route matches;
- router middleware runs only when a route matches;
- route/group middleware runs for the routes it is attached to.
Middleware has the standard signature func(http.Handler) http.Handler, so any net/http middleware works.
New returns a Router; Get, Post, Group and Resource register Route values whose handlers are web.HandlerFunc, and Router.Handler compiles them into an http.Handler. Apps receive the router in vukem.WithRoutes and reach named routes through Router.URL or the "route" template function.
See https://github.com/AlexGalhardo/vukem/blob/main/docs/http.md.
Example ¶
package main
import (
"fmt"
"log/slog"
"net/http"
"net/http/httptest"
"github.com/AlexGalhardo/vukem/router"
"github.com/AlexGalhardo/vukem/web"
)
func main() {
r := router.New(&web.Services{Log: slog.New(slog.DiscardHandler)})
r.Get("/posts/{id}", func(c *web.Ctx) error {
return c.String(http.StatusOK, "post "+c.Param("id"))
}).Name("posts.show")
h, err := r.Handler()
if err != nil {
fmt.Println(err)
return
}
rec := httptest.NewRecorder()
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/posts/42", http.NoBody))
fmt.Println(rec.Code, rec.Body.String())
link, err := r.URL("posts.show", "id", 7)
fmt.Println(link, err)
}
Output: 200 post 42 /posts/7 <nil>
Index ¶
- func Adapt(h web.HandlerFunc, services *web.Services) http.Handler
- func Wrap(h http.Handler) web.HandlerFunc
- type Creator
- type Destroyer
- type Editor
- type Group
- func (g *Group) APIResource(name string, ctrl any) *Resource
- func (g *Group) Any(path string, h web.HandlerFunc) *Route
- func (g *Group) Delete(path string, h web.HandlerFunc) *Route
- func (g *Group) ErrorHandler(wrap func(next web.ErrorHandler) web.ErrorHandler) *Group
- func (g *Group) Get(path string, h web.HandlerFunc) *Route
- func (g *Group) Group(prefix string, fn func(g *Group)) *Group
- func (g *Group) Handle(pattern string, h http.Handler) *Route
- func (g *Group) Match(methods []string, path string, h web.HandlerFunc) []*Route
- func (g *Group) Name(prefix string) *Group
- func (g *Group) Only() *Group
- func (g *Group) Options(path string, h web.HandlerFunc) *Route
- func (g *Group) Patch(path string, h web.HandlerFunc) *Route
- func (g *Group) Post(path string, h web.HandlerFunc) *Route
- func (g *Group) Put(path string, h web.HandlerFunc) *Route
- func (g *Group) Resource(name string, ctrl any) *Resource
- func (g *Group) Use(mw ...Middleware) *Group
- type Indexer
- type Middleware
- type Resource
- type Route
- type RouteInfo
- type Router
- func (r *Router) APIResource(name string, ctrl any) *Resource
- func (r *Router) Any(path string, h web.HandlerFunc) *Route
- func (r *Router) Compiled() bool
- func (r *Router) Delete(path string, h web.HandlerFunc) *Route
- func (r *Router) Get(path string, h web.HandlerFunc) *Route
- func (r *Router) Group(prefix string, fn func(g *Group)) *Group
- func (r *Router) Handle(pattern string, h http.Handler) *Route
- func (r *Router) Handler() (http.Handler, error)
- func (r *Router) Host(host string, fn func(g *Group)) *Group
- func (r *Router) Match(methods []string, path string, h web.HandlerFunc) []*Route
- func (r *Router) Options(path string, h web.HandlerFunc) *Route
- func (r *Router) Patch(path string, h web.HandlerFunc) *Route
- func (r *Router) Post(path string, h web.HandlerFunc) *Route
- func (r *Router) Put(path string, h web.HandlerFunc) *Route
- func (r *Router) Resource(name string, ctrl any) *Resource
- func (r *Router) Routes() []RouteInfo
- func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
- func (r *Router) Services() *web.Services
- func (r *Router) URL(name string, params ...any) (string, error)
- func (r *Router) Use(mw ...Middleware)
- func (r *Router) UseServer(mw ...Middleware)
- type Shower
- type Storer
- type Updater
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Group ¶
type Group struct {
// contains filtered or unexported fields
}
Group is a set of routes sharing a path prefix, a name prefix and middleware. Groups nest.
func (*Group) APIResource ¶
APIResource is Resource without the create and edit form routes.
func (*Group) Any ¶
func (g *Group) Any(path string, h web.HandlerFunc) *Route
Any registers path for every method.
func (*Group) Delete ¶
func (g *Group) Delete(path string, h web.HandlerFunc) *Route
Delete registers a DELETE route.
func (*Group) ErrorHandler ¶ added in v0.34.0
func (g *Group) ErrorHandler(wrap func(next web.ErrorHandler) web.ErrorHandler) *Group
ErrorHandler sets how errors are rendered for the group's routes, nested groups included, whether a handler returned them or a middleware failed through web.Fail or web.ServicesFrom. wrap receives the handler that would run otherwise (an outer group's, the router's Services.Errors, or web.DefaultErrorHandler) and returns its replacement, so it may decorate the response and delegate:
api.ErrorHandler(func(next web.ErrorHandler) web.ErrorHandler {
return func(c *web.Ctx, err error) {
c.Writer().Header().Set("Cache-Control", "no-store")
next(c, err)
}
})
wrap runs on every request of the group, so keep it cheap. A later call replaces an earlier one; 404 and 405 answers for unmatched paths keep the router's handler.
func (*Group) Get ¶
func (g *Group) Get(path string, h web.HandlerFunc) *Route
Get registers a GET route (it also answers HEAD).
func (*Group) Only ¶ added in v0.36.0
Only makes a Router.Host group the only thing its host serves: a request for that host that matches none of the group's routes gets a 404 (or 405 with Allow when another method matches) from the error handler, never a main route. Those answers skip the router middleware (Use: the web stack, sessions, CSRF) but keep the group's ErrorHandler, so an API host can answer problem+json. On a group without a host it is a Handler error.
func (*Group) Options ¶
func (g *Group) Options(path string, h web.HandlerFunc) *Route
Options registers an OPTIONS route.
func (*Group) Patch ¶
func (g *Group) Patch(path string, h web.HandlerFunc) *Route
Patch registers a PATCH route.
func (*Group) Post ¶
func (g *Group) Post(path string, h web.HandlerFunc) *Route
Post registers a POST route.
func (*Group) Put ¶
func (g *Group) Put(path string, h web.HandlerFunc) *Route
Put registers a PUT route.
func (*Group) Resource ¶
Resource registers RESTful routes for ctrl under /name. Nested resources use dots: "posts.comments" → /posts/{post}/comments/{comment}. Route names are "<name>.<action>".
func (*Group) Use ¶
func (g *Group) Use(mw ...Middleware) *Group
Use appends middleware applied to every route of the group (including routes registered before the call and in nested groups).
type Middleware ¶
Middleware wraps an http.Handler.
type Resource ¶
type Resource struct {
// contains filtered or unexported fields
}
Resource is a set of routes generated from a controller.
func (*Resource) Shallow ¶
Shallow makes the member routes of a nested resource top-level, as their IDs are unique on their own: /posts/{post}/comments stays for index/create/store, while show/edit/update/destroy become /comments/{comment}. Route names do not change.
func (*Resource) Use ¶
func (rs *Resource) Use(mw ...Middleware) *Resource
Use applies middleware to the resource routes (all actions when none are given by Only/Except before).
type Route ¶
type Route struct {
// contains filtered or unexported fields
}
Route is a registered route.
func (*Route) Doc ¶ added in v0.15.0
Doc attaches documentation (e.g. an api.Operation) that generators such as the OpenAPI exporter read back through RouteInfo.Doc.
func (*Route) Host ¶ added in v0.33.0
Host returns the host the route is restricted to (see Router.Host), or "".
func (*Route) Name ¶
Name sets the route name used by URL generation. The names of enclosing groups are prefixed when the router is compiled.
type RouteInfo ¶
type RouteInfo struct {
Method string `json:"method"`
// Host is the host the route is restricted to (Router.Host), or "".
Host string `json:"host,omitempty"`
Path string `json:"path"`
Name string `json:"name,omitempty"`
// LocalName is the name given to the route itself, without the group
// name prefixes (Name is the full name).
LocalName string `json:"-"`
Middleware []string `json:"middleware,omitempty"`
Doc any `json:"-"`
}
RouteInfo describes a compiled route.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router collects routes and compiles them into an http.Handler.
func (*Router) APIResource ¶
APIResource is Resource without the create and edit form routes.
func (*Router) Any ¶
func (r *Router) Any(path string, h web.HandlerFunc) *Route
Any registers a route for every method.
func (*Router) Compiled ¶ added in v0.25.0
Compiled reports whether the routes were compiled (by Handler, URL or a first request). Routes added after that are not served.
func (*Router) Delete ¶
func (r *Router) Delete(path string, h web.HandlerFunc) *Route
Delete registers a DELETE route.
func (*Router) Get ¶
func (r *Router) Get(path string, h web.HandlerFunc) *Route
Get registers a GET route (it also answers HEAD).
func (*Router) Group ¶
Group creates a route group under prefix. Inside it, g.Get("/", h) serves prefix+"/" exactly (ServeMux redirects the bare prefix there) and g.Get("", h) serves the bare prefix; neither is a catch-all.
Example ¶
package main
import (
"fmt"
"log/slog"
"net/http"
"github.com/AlexGalhardo/vukem/router"
"github.com/AlexGalhardo/vukem/web"
)
func main() {
r := router.New(&web.Services{Log: slog.New(slog.DiscardHandler)})
r.Group("/admin", func(g *router.Group) {
g.Get("/dashboard", func(c *web.Ctx) error {
return c.String(http.StatusOK, "dashboard")
}).Name("dashboard")
}).Name("admin.")
for _, route := range r.Routes() {
fmt.Println(route.Method, route.Path, route.Name)
}
}
Output: GET /admin/dashboard admin.dashboard
func (*Router) Handle ¶
Handle mounts a plain http.Handler. pattern uses ServeMux syntax and may start with a method ("GET /metrics").
func (*Router) Handler ¶
Handler compiles the routes. It reports pattern conflicts, invalid patterns and duplicate names. Routes added afterwards are ignored.
func (*Router) Host ¶ added in v0.33.0
Host creates a group whose routes match only requests for host (any port), e.g. r.Host("api.example.com", func(g *router.Group) {...}) next to the same paths on the main host. Nested groups keep the host. host is a lowercase name without port, path or wildcard; anything else is a Handler error. URL returns the scheme-relative "//host/path" for these routes, so links work from any host.
func (*Router) Options ¶
func (r *Router) Options(path string, h web.HandlerFunc) *Route
Options registers an OPTIONS route.
func (*Router) Patch ¶
func (r *Router) Patch(path string, h web.HandlerFunc) *Route
Patch registers a PATCH route.
func (*Router) Post ¶
func (r *Router) Post(path string, h web.HandlerFunc) *Route
Post registers a POST route.
func (*Router) Put ¶
func (r *Router) Put(path string, h web.HandlerFunc) *Route
Put registers a PUT route.
func (*Router) Resource ¶
Resource registers the actions ctrl implements (see Indexer, Storer, ...).
func (*Router) ServeHTTP ¶
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
ServeHTTP compiles on first use and serves; it panics on a build error, so call Handler at boot to surface errors early.
func (*Router) URL ¶
URL implements web.URLGenerator: params are name/value pairs filling the route's {wildcards}. Missing or unknown params are errors.
func (*Router) Use ¶
func (r *Router) Use(mw ...Middleware)
Use appends middleware that runs for every matched route.
func (*Router) UseServer ¶
func (r *Router) UseServer(mw ...Middleware)
UseServer appends middleware that runs for every request.