rout

package module
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Sep 5, 2023 License: Unlicense Imports: 11 Imported by: 0

README

Overview

Experimental router for Go HTTP servers. Imperative control flow with declarative syntax. Doesn't need middleware.

Very simple, small, dependency-free, reasonably fast.

Recommended in conjunction with github.com/mitranim/goh, which implements various "response" types that satisfy http.Handler.

API docs: https://pkg.go.dev/github.com/mitranim/rout.

Performance: a moderately-sized routing table of a production app can take a few microseconds, with very minimal allocations.

Examples: see below.

TOC

Why

  • You want a router because "manual" routing requires too much code.
  • Most routing libraries are fatally flawed. They sacrifice imperative control flow, then invent "middleware" to work around the resulting problems. Imperative flow is precious. Treasure it. Don't let it go.

rout is an evolution of "manual" routing that avoids common router flaws:

  • Control flow is still imperative. It doesn't need middleware: simply call A before/after B.
  • No "mounting". Routing always uses full URL paths: /a/b/c instead of "/a" "/b" "/c". This makes the code searchable.
  • Correct "not found" and "method not allowed" semantics out of the box.
  • Supports multiple ways of pattern matching: exact, prefix, OAS-style pattern, and regexp. Patterns are compiled once and cached.

The resulting code is very dense, simple, and clear.

Usage

import (
  "net/http"

  "github.com/mitranim/goh"
  "github.com/mitranim/rout"
)

type (
  Rew = http.ResponseWriter
  Req = *http.Request
  Han = http.Handler
)

// Top-level handler, oversimplified. See docs on `Rou`.
var handler http.Handler = rout.RouFunc(routes)

/*
This is executed for every request.

Unknown paths cause the router to return error 404. Unknown methods on known
paths cause the router to return error 405. The error is handled by YOUR code,
which is an important advantage; see the handlers above.
*/
func routes(rou rout.Rou) {
  rou.Pat(`/`).Get().Han(pageIndex)
  rou.Pat(`/articles`).Get().Han(pageArticles)
  rou.Pat(`/articles/{}`).Get().ParamHan(pageArticle)
  rou.Sta(`/api`).Sub(routesApi)
  rou.Get().Handler(fileServer)
}

var fileServer = http.FileServer(http.Dir(`public`))

// This is executed for every request that gets routed to it.
func routesApi(rou rout.Rou) {
  /**
  Enable CORS only for this route. This would usually involve middleware.
  With `rout`, you just call A before B.
  */
  allowCors(rou.Rew.Header())

  rou.Sta(`/api/articles`).Sub(routesApiArticles)
}

// This is executed for every request that gets routed to it.
func routesApiArticles(rou rout.Rou) {
  rou.Pat(`/api/articles`).Methods(func(rou rout.Rou) {
    rou.Get().Han(apiArticleFeed)
    rou.Post().Han(apiArticleCreate)
  })
  rou.Pat(`/api/articles/{}`).Methods(func(rou rout.Rou) {
    rou.Get().ParamHan(apiArticleGet)
    rou.Patch().ParamHan(apiArticleUpdate)
    rou.Delete().ParamHan(apiArticleDelete)
  })
}

// Oversimplified for example's sake.
func allowCors(head http.Header)                  {}
func pageIndex(req Req) Han                       { return goh.StringOk(`ok`) }
func pageArticles(req Req) Han                    { return goh.StringOk(`ok`) }
func pageArticle(req Req, args []string) Han      { return goh.StringOk(`ok`) }
func apiArticleFeed(req Req) Han                  { return goh.StringOk(`ok`) }
func apiArticleCreate(req Req) Han                { return goh.StringOk(`ok`) }
func apiArticleGet(req Req, args []string) Han    { return goh.StringOk(`ok`) }
func apiArticleUpdate(req Req, args []string) Han { return goh.StringOk(`ok`) }
func apiArticleDelete(req Req, args []string) Han { return goh.StringOk(`ok`) }

Changelog

v0.8.0

ErrStatus no longer falls back on status 500. Callers of ErrStatus must check if the status is 0 and implement their own fallback. Use the newly added ErrStatusFallback for the old behavior.

v0.7.1

Renamed Pat.Append to Pat.AppendTo for consistency with other libraries.

v0.7.0

Added Rou.Mut for introspection. It stores the matched Endpoint after a successful match. Minor breaking change: Rou.Done is removed, as the boolean is now part of Mut. There is no measurable performance regression.

v0.6.3

Fix panic in ErrStatus when unwrapping non-comparable error values.

v0.6.2

On successful match, Rou no longer uses panics to break the flow. Instead it continues execution, but flips a hidden flag that causes it to ignore all further routes. This avoids some weird gotchas related to nil panics previously used by this library.

Performance: this forces a single tiny allocation (new(bool)), but appears to marginally improve routing performance.

v0.6.1

Bugfix for parametrized pattern matching in method-only routes.

v0.6.0

  • Support OAS-style patterns such as /one/{}/two.
    • Add Pat.
    • Add Rou.Pat.
  • Add tools for introspection via "dry runs":
    • Visit
    • Visitor
    • RegexpVisitor
    • PatternVisitor
    • Ident
    • IdentType
    • NopRew
  • Various breaking renamings for brevity:
    • RouterRou.
    • ExactExa.
    • BeginSta.
    • RegexReg.
  • Export lower-level pattern-matching tools via Match.

v0.5.0

Lexicon change: "Res" → "Han" for anything that involves http.Handler.

Add support for *http.Response via Respond, Router.Res, Router.ParamRes. Expressing responses with http.Handler remains the preferred and recommended approach.

v0.4.4

Optimize error creation: hundreds of nanoseconds → tens of nanoseconds.

v0.4.3

Exported ErrStatus.

v0.4.2

WriteErr and Router.Serve now perform deep error unwrapping to obtain the HTTP status code of an error.

v0.4.1

Router.Res and Router.ParamRes are now variadic, accepting multiple funcs. They try the funs sequentially until one of the funcs returns a non-nil handler. Also added Coalesce which provides similar behavior without a router.

v0.4.0

Support multiple ways of URL matching:

  • Router.Regex → by regexp (supports capture groups)
  • Router.Exact → by exact match (no capture groups)
  • Router.Begin → by prefix (no capture groups)

Breaking: renamed Router.RegRouter.Regex for symmetry with the other path pattern methods.

Most routes can be expressed with exact or prefix matches. Regexps are needed only for capturing args. This makes routing simpler, clearer, and less error-prone. It also significantly improves performance, compared to using regexps for everything.

v0.3.0

Added simple shortcuts:

  • WriteErr
  • Router.Route
  • Router.Serve

Breaking: Route has been replaced with Router.Route.

v0.2.1

Res now implements http.Handler. This is not used internally, but could be handy for users.

v0.2.0

API redesign: fewer types, simpler, more flexible.

v0.1.1

Method matching is now case-insensitive.

v0.1.0

First tagged release.

License

https://unlicense.org

Misc

I'm receptive to suggestions. If this library almost satisfies you but needs changes, open an issue or chat me up. Contacts: https://mitranim.com/#contacts

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrInit = fmt.Errorf(
	`[rout] routing error: the router wasn't properly initialized; please use "rout.MakeRou"`,
)

Returned by `rout.Route` when the router wasn't properly initialized. Using `rout.MakeRou` avoids this.

Functions

func Err

func Err(msg string, status int, meth, path string) string

Generates a routing error message including the given status, method and path. More efficient than equivalent `fmt.Sprintf` or `fmt.Errorf`.

func ErrStatus added in v0.4.3

func ErrStatus(err error) int

Returns the underlying HTTP status code of the given error, relying on the following hidden interface which is implemented by `rout.Err`. The interface may be implemented by deeply-wrapped errors; this performs deep unwrapping.

interface { HttpStatusCode() int }

If the error is nil or doesn't implement this interface, status is 0. If you always want a non-zero code, use `ErrStatusFallback` which falls back on 500.

func ErrStatusFallback added in v0.8.0

func ErrStatusFallback(err error) int

Convenience wrapper for `ErrStatus` that falls back on status 500 when the error doesn't seem to contain an HTTP status, always returning a non-zero result.

func Ident added in v0.6.0

func Ident(val interface{}) [2]uintptr

Tool for introspection. Returns the "identity" of the input: the internal representation of the interface value that was passed in. When performing a "dry run" via `Visit`, this function generates the identity of route handlers. Advanced users of this package may build a registry that maps handler identities to arbitrary metadata, and retrieve that information from visited routes, using idents as keys.

func IdentType added in v0.6.0

func IdentType(val [2]uintptr) r.Type

Tool for introspection. Returns the original `reflect.Type` of an `Ident`. If the input is zero, the returned type is nil.

func Respond added in v0.5.0

func Respond(rew http.ResponseWriter, res *http.Response) error

Writes the given response. Used internally by `Rou.Res` and `Rou.ParamRes`. If either the response writer or the response is nil, this is a nop. Uses `res.Header`, `res.StatusCode`, and `res.Body`, ignoring all other fields of the response. The returned error, if any, always comes from copying the body via `io.Copy`, and should occur mostly due to premature client disconnect.

func Visit added in v0.6.0

func Visit(fun func(Rou), vis Visitor)

Tool for introspection. Performs a "dry run" of the given routing function, visiting all routes without executing any handlers. During the dry run, the `http.ResponseWriter` contained in the router is a special nop type that discards all writes.

func WriteErr added in v0.3.0

func WriteErr(rew http.ResponseWriter, err error)

Shortcut for top-level error handling. If the error is nil, do nothing. If the error is non-nil, write its message as plain text. HTTP status code is obtained via `rout.ErrStatusFallback`.

Example:

rout.WriteErr(rew, rout.MakeRou(rew, req).Route(myRoutes))

Types

type Coalesce added in v0.4.1

type Coalesce []Han

HTTP handler type that stores multiple `Han` functions, and when serving HTTP, uses the first non-nil `http.Handler` returned by one of those functions.

func (Coalesce) Han added in v0.5.0

func (self Coalesce) Han(req *http.Request) http.Handler

Invokes the funcs in order, returning the first resulting non-nil handler.

func (Coalesce) ServeHTTP added in v0.4.1

func (self Coalesce) ServeHTTP(rew http.ResponseWriter, req *http.Request)

Implement `http.Handler`.

type Endpoint added in v0.6.0

type Endpoint struct {
	Pattern string
	Match   Match
	Method  string
	Handler [2]uintptr
}

Tool for introspection. Passed to `Visitor` when performing a "dry run" via the `Visit` function.

type ErrMethodNotAllowed added in v0.4.4

type ErrMethodNotAllowed string

Error type returned by `rout.Route` for requests with a known path and an unknown method.

func MethodNotAllowed added in v0.4.4

func MethodNotAllowed(meth, path string) ErrMethodNotAllowed

Generates an appropriate `ErrMethodNotAllowed`. Used internally.

func (ErrMethodNotAllowed) Error added in v0.4.4

func (self ErrMethodNotAllowed) Error() string

Implement `error` by returning self.

func (ErrMethodNotAllowed) HttpStatusCode added in v0.4.4

func (ErrMethodNotAllowed) HttpStatusCode() int

Implement a hidden interface supported by `rout.ErrStatus`. Always returns `http.StatusMethodNotAllowed`.

type ErrNotFound added in v0.4.4

type ErrNotFound string

Error type returned by `rout.Route` for requests with an unknown path.

func NotFound added in v0.4.4

func NotFound(meth, path string) ErrNotFound

Generates an appropriate `ErrNotFound`. Used internally.

func (ErrNotFound) Error added in v0.4.4

func (self ErrNotFound) Error() string

Implement `error` by returning self.

func (ErrNotFound) HttpStatusCode added in v0.4.4

func (ErrNotFound) HttpStatusCode() int

Implement a hidden interface supported by `rout.ErrStatus`. Always returns `http.StatusNotFound`.

type Func

type Func = func(http.ResponseWriter, *http.Request)

Type of functions passed to `Rou.Func`. Non-parametrized handler func. Same signature as `http.HandlerFunc`, but this is an anonymous type, not a typedef. Doesn't implement `http.Handler`.

type Han added in v0.5.0

type Han = func(*http.Request) http.Handler

Type of functions passed to `Rou.Han`. Short for "handler" or "handlerer". The returned `http.Handler` is used to write the response. To represent responses with handlers, use "github.com/mitranim/goh".

type Match added in v0.6.0

type Match byte

Various types of pattern matching supported by this package: exact, start/prefix, regexp, OAS-style pattern. See the comments on the constants such as `MatchExa`.

const (
	/**
	Short for "exact". Used by `Rou.Exa`. Compares pattern and input via `==`.
	Doesn't support capture groups; `.Submatch` returns `[]string{}` on a match.
	As a special rule, the empty pattern “ matches any input.
	*/
	MatchExa Match = iota

	/**
	Short for "start", or "starts with", or "prefix". Used by `Rou.Sta`. When
	matching, requires that the input path has the given pattern as its prefix.
	Because "net/http" ensures that request paths begin with `/`, the prefix
	should also begin with `/`, but it doesn't need to end with `/`; this
	package takes care of that. Doesn't support capture groups; `.Submatch`
	returns `[]string{}` on a match. As a special rule, the empty pattern “
	matches any input.
	*/
	MatchSta

	/**
	Short for "regexp". Used by `Rou.Reg`. Performs matching or submatching by
	converting its pattern to `*regexp.Regexp`. Compiles each pattern only once,
	with caching and reuse. Does support capture groups. The empty pattern “
	matches any input.
	*/
	MatchReg

	/**
	Short for "pattern", specifically path pattern compatible with OpenAPI specs.
	Used by `Rou.Pat`. Performs matching or submatching by converting its
	pattern to `Pat`, which is also exported by this package. Compiles each
	pattern only once, with caching and reuse. Does support capture groups. The
	empty pattern “ matches any input.
	*/
	MatchPat
)

func (Match) Match added in v0.6.0

func (self Match) Match(pat, inp string) bool

True if the pattern matches the input. See the comments on the various `Match` constants.

func (Match) String added in v0.6.0

func (self Match) String() string

Implement `fmt.Stringer` for debug purposes.

func (Match) Submatch added in v0.6.0

func (self Match) Submatch(pat, inp string) []string

If the pattern matches the input, returns a non-nil slice of captures. Otherwise returns nil. See the comments on the various `Match` constants. Regardless of the match implementation, captures start at index 0, not at index 1 like in regexps.

type Mut added in v0.7.0

type Mut struct {
	Endpoint Endpoint
	Done     bool
}

Mutable part of `Rou`, shared between all instances of `Rou` for a given request-response. Other fields of `Rou` are considered immutable. See `Rou` and its "builder" methods. After a successful route match, `.Done` is true and `.Endpoint` describes the matched route.

type NopRew added in v0.6.0

type NopRew struct{}

Nop implementation of `http.ResponseWriter` used internally by `Visit`. Exported for implementing custom variants of `Visit`.

func (NopRew) Header added in v0.6.0

func (NopRew) Header() http.Header

func (NopRew) Write added in v0.6.0

func (NopRew) Write(val []byte) (int, error)

func (NopRew) WriteHeader added in v0.6.0

func (NopRew) WriteHeader(int)

type ParamFunc

type ParamFunc = func(http.ResponseWriter, *http.Request, []string)

Type of functions passed to `Rou.ParamFunc`. Parametrized handler func. Takes additional args produced by capture groups, which are supported by `Rou.Reg` and `Rou.Pat`. Args start at index 0, not 1 like in a regexp match.

type ParamHan added in v0.5.0

type ParamHan = func(*http.Request, []string) http.Handler

Type of functions passed to `Rou.ParamHan`. Short for "parametrized handler/handlerer".

type ParamRes added in v0.2.0

type ParamRes = func(*http.Request, []string) *http.Response

Type of functions passed to `Rou.ParamRes`. Short for "parametrized responder".

type Pat added in v0.6.0

type Pat []string

Short for "pattern", specifically templated URL pattern compatible with OpenAPI:

/
/{}
/one/{id}
/one/{}/two/{}
/one/{id}/two/{action}
...
https://spec.openapis.org/oas/v3.1.0#path-templating

Supports parsing, matching, and capturing. MUCH more efficient than equivalent Go regexps. Once parsed, the pattern is safe for concurrent use by multiple goroutines.

This structure represents a pattern parsed via `(*Pat).Parse`. Empty strings represent capture groups, which are called "template expression" in OpenAPI. Non-empty strings represent exact matches. Template expressions / capture groups can't overlap. The current implementation allows only up to 8 capture groups; this is easy to optimize and serves the common case well. The limitation could be lifted if there was any demand.

Rules:

  • A non-empty segment matches and consumes the exact same string from the start of the input (a prefix), without capturing.

  • An empty segment matches, consumes, and captures an equivalent of the regular expression `([^/?#]+)`.

  • The pattern matches the entire input, behaving like a regexp wrapped in `^$`.

Just like `*regexp.Regexp`, `Pat` allows names in capture groups, such as "{id}", but discards them when parsing. Submatching is positional, by index.

func (Pat) AppendTo added in v0.7.1

func (self Pat) AppendTo(buf []byte) []byte

Appends a text representation, same as `.String`. Sometimes allows more efficient encoding.

func (Pat) MarshalText added in v0.6.0

func (self Pat) MarshalText() ([]byte, error)

Implement `encoding.TextMarshaler`, allowing automatic encoding to text, such as for JSON.

func (Pat) Match added in v0.6.0

func (self Pat) Match(inp string) bool

Like `(*regexp.Regexp).MatchString`: returns true if the input matches the pattern, without capturing.

func (Pat) Num added in v0.6.0

func (self Pat) Num() int

Same as `(*regexp.Regexp).NumSubexp`. Returns the amount of "capture groups" by counting empty segments.

func (*Pat) Parse added in v0.6.0

func (self *Pat) Parse(src string) error

Parses the pattern from a string, appending to the receiver.

func (Pat) Reg added in v0.6.0

func (self Pat) Reg() string

Returns a string representing a regexp pattern that should be equivalent to the given OAS pattern. The pattern is enclosed in `^$`. Template expressions such as "{}" or "{id}" are represented with `([^/?#]+)`. Because the pattern type has no way to store the text inside template expressions, the capture groups in the resulting regexp are anonymous.

func (Pat) String added in v0.6.0

func (self Pat) String() string

Implement `fmt.Stringer` for debug purposes. For patterns parsed from a string, the resulting representation is functionally equivalent to the original, but capture groups are now anonymous (their inner text is lost).

func (Pat) Submatch added in v0.6.0

func (self Pat) Submatch(inp string) []string

Similar to `(*regexp.Regexp).FindStringSubmatch`: returns nil or positional captures. Unlike regexps, the resulting slice has ONLY the captures, without the matched string. On success, slice length equals `pat.Num()`. Allows only a limited number of captures; see the comment on the type.

func (*Pat) UnmarshalText added in v0.6.0

func (self *Pat) UnmarshalText(src []byte) error

Implement `encoding.TextUnmarshaler`, allowing automatic decoding from text, such as from JSON.

type PatternVisitor added in v0.6.0

type PatternVisitor [1]SimpleVisitor

Tool for introspection. Adapter between `Visitor` and `SimpleVisitor`. Converts route patterns to OAS-style patterns compatible with `Pat`, passing those to the inner visitor.

func (PatternVisitor) Endpoint added in v0.6.0

func (self PatternVisitor) Endpoint(val Endpoint)

Implement `Visitor`.

type R

type R = Rou

Shortcut for brevity.

type RegexpVisitor added in v0.6.0

type RegexpVisitor [1]SimpleVisitor

Tool for introspection. Adapter between `Visitor` and `SimpleVisitor`. Converts route patterns to regexp patterns, passing those to the inner visitor.

func (RegexpVisitor) Endpoint added in v0.6.0

func (self RegexpVisitor) Endpoint(val Endpoint)

Implement `Visitor`.

type Res added in v0.2.0

type Res = func(*http.Request) *http.Response

Type of functions passed to `Rou.Res`. Short for "responder". The returned `*http.Response` is sent back via the function `Respond`.

type Rou added in v0.6.0

type Rou struct {
	Rew        http.ResponseWriter
	Req        *http.Request
	Mut        *Mut
	Vis        Visitor
	Method     string
	Pattern    string
	Style      Match
	OnlyMethod bool
}

Router type. Matches patterns and executes handlers. Should be used via `Rou.Serve` or `Rou.Route`, which handles panics inherent to the routing flow. Immutable, with a builder-style API where every method returns a modified copy. A router is stack-allocated; its builder API incurs no allocator/GC work. The only tiny exception is `Rou.Mut`, which is the only allocation per request forced by this package.

Implementation note. All "modifying" methods are defined on the value type in order to return modified copies, but many non-modifying methods are defined on the pointer type for marginal efficiency gains, due to the size of this struct.

func MakeRou added in v0.6.0

func MakeRou(rew http.ResponseWriter, req *http.Request) Rou

Makes a router for the given request-response. Usage:

ro.MakeRou(rew, req).Serve(myRoutes)

ro.WriteErr(rew, ro.MakeRou(rew, req).Route(myRoutes))

func (Rou) Connect added in v0.6.0

func (self Rou) Connect() Rou

Same as `.Meth(http.MethodConnect)`. Returns a router that matches only this HTTP method.

func (Rou) Delete added in v0.6.0

func (self Rou) Delete() Rou

Same as `.Meth(http.MethodDelete)`. Returns a router that matches only this HTTP method.

func (Rou) Exa added in v0.6.0

func (self Rou) Exa(val string) Rou

Short for "exact". Takes a string and returns a router that tests `req.URL.Path` by matching this string exactly. Unlike `Rou.Reg`, this doesn't support capture groups; parametrized handlers will always receive empty `[]string{}`.

func (Rou) Func added in v0.6.0

func (self Rou) Func(fun Func)

If the router matches the request, use the given handler func to respond. If the router doesn't match the request, do nothing. The func may be nil. In "dry run" mode via `Visit`, this invokes a visitor for the current endpoint.

func (Rou) Get added in v0.6.0

func (self Rou) Get() Rou

Same as `.Meth(http.MethodGet)`. Returns a router that matches only this HTTP method.

func (Rou) Han added in v0.6.0

func (self Rou) Han(fun Han)

If the router matches the request, respond by using the handler returned by the given function. If the router doesn't match the request, do nothing. In "dry run" mode via `Visit`, this invokes a visitor for the current endpoint.

func (Rou) Handler added in v0.6.0

func (self Rou) Handler(val http.Handler)

If the router matches the request, use the given handler to respond. If the router doesn't match the request, do nothing. The handler may be nil. In "dry run" mode via `Visit`, this invokes a visitor for the current endpoint.

func (Rou) Head added in v0.6.0

func (self Rou) Head() Rou

Same as `.Meth(http.MethodHead)`. Returns a router that matches only this HTTP method.

func (*Rou) Match added in v0.6.0

func (self *Rou) Match() bool

Mostly for internal use. True if the router matches the request. If `.OnlyMethod` is true, matches only the request's method. Otherwise matches both the pattern and the method. If the pattern matches but the method doesn't, panics with `ErrMethodNotAllowed`; the panic is normally caught and returned via `Rou.Route`.

func (Rou) Meth added in v0.6.0

func (self Rou) Meth(val string) Rou

Short for "method". Returns a router that matches only the given method. If the method is empty, the resulting router matches all methods, which is the default. Note: to match multiple methods for one route, use `Rou.Methods`. Otherwise, the first mismatch generates `ErrMethodNotAllowed`.

func (Rou) MethodOnly added in v0.6.0

func (self Rou) MethodOnly() Rou

Returns a router set to "method only" mode.

In "normal" mode (default), whenever the router matches the URL pattern but doesn't match the HTTP method, it immediately generates a "method not allowed" error. All pattern-modifying router methods, such as `Rou.Exa`, automatically switch the router into "normal" mode.

In "method only" mode (opt-in), the router tests ONLY the HTTP method. The URL pattern is considered an automatic match. Additionally, a mismatch doesn't generate an error. This is used by `Rou.Methods`, which automatically switches the router into "method only" mode. However, for parametrized endpoints such as `Rou.ParamFunc`, if the method matches, the router also matches the pattern to get args/captures.

func (Rou) Methods added in v0.6.0

func (self Rou) Methods(fun func(Rou))

If the router matches the request, perform sub-routing. The router provided to the function is set to "method only" mode: a mismatch in the HTTP method doesn't immediately generate an error. However, if sub-routing doesn't find a match, this panics with `ErrMethodNotAllowed`. If the router doesn't match the request, do nothing.

func (Rou) Options added in v0.6.0

func (self Rou) Options() Rou

Same as `.Meth(http.MethodOptions)`. Returns a router that matches only this HTTP method.

func (Rou) ParamFunc added in v0.6.0

func (self Rou) ParamFunc(fun ParamFunc)

If the router matches the request, use the given handler func to respond. If the router doesn't match the request, do nothing. The func may be nil. The additional `[]string` argument contains regexp captures from the pattern passed to `Rou.Reg`, if any. In "dry run" mode via `Visit`, this invokes a visitor for the current endpoint.

func (Rou) ParamHan added in v0.6.0

func (self Rou) ParamHan(fun ParamHan)

If the router matches the request, respond by using the handler returned by the given function. If the router doesn't match the request, do nothing. The additional `[]string` argument contains regexp captures from the pattern passed to `Rou.Reg`, if any. In "dry run" mode via `Visit`, this invokes a visitor for the current endpoint.

func (Rou) ParamRes added in v0.6.0

func (self Rou) ParamRes(fun ParamRes)

If the router matches the request, use the given responder func to generate a response, and use `Respond` to write it. If the router doesn't match the request, do nothing. The func may be nil. The additional `[]string` argument contains regexp captures from the pattern passed to `Rou.Reg`, if any. In "dry run" mode via `Visit`, this invokes a visitor for the current endpoint.

func (Rou) Pat added in v0.6.0

func (self Rou) Pat(val string) Rou

Short for "pattern". Takes a "path template" compatible with OpenAPI and returns a router that will use this pattern to match `req.URL.Path`, via `Pat`. Patterns are compiled lazily, cached, and reused.

func (Rou) Patch added in v0.6.0

func (self Rou) Patch() Rou

Same as `.Meth(http.MethodPatch)`. Returns a router that matches only this HTTP method.

func (Rou) Post added in v0.6.0

func (self Rou) Post() Rou

Same as `.Meth(http.MethodPost)`. Returns a router that matches only this HTTP method.

func (Rou) Put added in v0.6.0

func (self Rou) Put() Rou

Same as `.Meth(http.MethodPut)`. Returns a router that matches only this HTTP method.

func (Rou) Reg added in v0.6.0

func (self Rou) Reg(val string) Rou

Short for "regexp". Takes a regexp pattern and returns a router that will use this pattern to match `req.URL.Path`. Regexps are compiled lazily, cached, and reused.

func (Rou) Res added in v0.6.0

func (self Rou) Res(fun Res)

If the router matches the request, use `Respond` to write the response returned by the given function. If the router doesn't match the request, do nothing. In "dry run" mode via `Visit`, this invokes a visitor for the current endpoint.

func (Rou) Route added in v0.6.0

func (self Rou) Route(fun func(Rou)) (err error)

Routes the given request-response, recovering from panics inherent to the routing flow of this package. The resulting error is usually of type `Err`, containing an appropriate HTTP status code. Your code must handle the error, sending back an appropriate response. If routing was performed successfully, the error is nil.

Same as `Rou.Sub`, but catches panics, returning them as errors.

Example
package main

import (
	"io"
	"net/http"

	"github.com/mitranim/rout"
)

type (
	Rew = http.ResponseWriter
	Req = *http.Request
	Res = *http.Response
	Han = http.Handler
)

func main() {
	handleRequest(nil, nil)
}

// Top-level request handler.
func handleRequest(rew Rew, req Req) {
	// Errors are handled ONLY in app code. There are no surprises.
	err := rout.MakeRou(rew, req).Route(routes)

	// Replace this with custom error handling.
	rout.WriteErr(rew, err)
}

/*
This is executed for every request.

Unknown paths cause the router to return error 404. Unknown methods on known
paths cause the router to return error 405. The error is handled by YOUR code,
which is an important advantage; see `handleRequest` above.
*/
func routes(rou rout.Rou) {
	rou.Pat(`/`).Get().Han(pageIndex)
	rou.Pat(`/articles`).Get().Han(pageArticles)
	rou.Pat(`/articles/{}`).Get().ParamHan(pageArticle)
	rou.Sta(`/api`).Sub(routesApi)
	rou.Get().Handler(fileServer)
}

var fileServer = http.FileServer(http.Dir(`public`))

// This is executed for every request that gets routed to it.
func routesApi(rou rout.Rou) {
	/**
	Enable CORS only for this route. This would usually involve middleware.
	With `rout`, you just call A before B.
	*/
	allowCors(rou.Rew.Header())

	rou.Sta(`/api/articles`).Sub(routesApiArticles)
}

// This is executed for every request that gets routed to it.
func routesApiArticles(rou rout.Rou) {
	rou.Pat(`/api/articles`).Methods(func(rou rout.Rou) {
		rou.Get().Han(apiArticleFeed)
		rou.Post().Han(apiArticleCreate)
	})
	rou.Pat(`/api/articles/{}`).Methods(func(rou rout.Rou) {
		rou.Get().ParamHan(apiArticleGet)
		rou.Patch().ParamHan(apiArticleUpdate)
		rou.Delete().ParamHan(apiArticleDelete)
	})
}

// Oversimplified for example's sake.
func allowCors(head http.Header)                  {}
func pageIndex(req Req) Han                       { return Str(`ok`) }
func pageArticles(req Req) Han                    { return Str(`ok`) }
func pageArticle(req Req, args []string) Han      { return Str(`ok`) }
func apiArticleFeed(req Req) Han                  { return Str(`ok`) }
func apiArticleCreate(req Req) Han                { return Str(`ok`) }
func apiArticleGet(req Req, args []string) Han    { return Str(`ok`) }
func apiArticleUpdate(req Req, args []string) Han { return Str(`ok`) }
func apiArticleDelete(req Req, args []string) Han { return Str(`ok`) }

type Str string

func (self Str) ServeHTTP(rew Rew, _ Req) { _, _ = io.WriteString(rew, string(self)) }
func (self Str) Ptr() *Str                { return &self }

func (Rou) Serve added in v0.6.0

func (self Rou) Serve(fun func(Rou))

Shortcut for routing with default error handling. Same as `rout.Rou.Route`, but instead of returning an error, uses `rout.WriteErr` to write it. Example:

rout.MakeRou(rew, req).Serve(myRoutes)

func (Rou) Sta added in v0.6.0

func (self Rou) Sta(val string) Rou

Short for "start" or "starts with". Takes a string and returns a router that tests `req.URL.Path` by requiring that it has the given prefix. Because "net/http" ensures that request path begins with `/`, the prefix should also begin with `/`, but it doesn't need to end with `/`. Unlike `Rou.Reg`, this doesn't support capture groups; parametrized handlers will always receive empty `[]string{}`.

func (Rou) Sub added in v0.6.0

func (self Rou) Sub(fun func(Rou))

If the router matches the request, perform sub-routing. If sub-routing doesn't find a match, panic with `ErrNotFound`. If the router doesn't match the request, do nothing.

func (*Rou) Submatch added in v0.6.0

func (self *Rou) Submatch() []string

Mostly for internal use. Like `Rou.Match`, but instead of a boolean, returns a slice with captured args. If there's no match, the slice is nil. Otherwise, the slice is non-nil, and its length equals the amount of capture groups in the current pattern. If the pattern matches but the method doesn't, panics with `ErrMethodNotAllowed`; the panic is normally caught and returned via `Rou.Route`.

func (Rou) Trace added in v0.6.0

func (self Rou) Trace() Rou

Same as `.Meth(http.MethodTrace)`. Returns a router that matches only this HTTP method.

type RouFunc added in v0.6.0

type RouFunc func(Rou)

Short for "routing function". Type of functions passed to the various routing methods such as `Rou.Route`. Also implements `http.Handler`, as a shortcut for using `MakeRou` and `Serve`, passing itself as the routing func.

func (RouFunc) ServeHTTP added in v0.6.0

func (self RouFunc) ServeHTTP(rew http.ResponseWriter, req *http.Request)

Implement `http.Handler`.

type SimpleVisitor added in v0.6.0

type SimpleVisitor interface {
	Endpoint(pattern, method string, ident [2]uintptr)
}

Tool for introspection. Simplified version of `Visitor` that doesn't "know" about the multiple pattern types supported by this package. Must be wrapped by adapters such as `RegexpVisitor` and `PatternVisitor`. WTB better name.

type SimpleVisitorFunc added in v0.6.0

type SimpleVisitorFunc func(pattern, method string, ident [2]uintptr)

Shortcut type. Implements `SimpleVisitor` by calling itself.

func (SimpleVisitorFunc) Endpoint added in v0.6.0

func (self SimpleVisitorFunc) Endpoint(pattern, method string, ident [2]uintptr)

Implement `SimpleVisitor` by calling itself.

type Visitor added in v0.6.0

type Visitor interface{ Endpoint(Endpoint) }

Tool for introspection. Used for performing a "dry run" that visits all routes without executing the handlers. See `Visit`.

type VisitorFunc added in v0.6.0

type VisitorFunc func(Endpoint)

Shortcut type. Implements `Visitor` by calling itself.

func (VisitorFunc) Endpoint added in v0.6.0

func (self VisitorFunc) Endpoint(val Endpoint)

Implement `Visitor` by calling itself.

Jump to

Keyboard shortcuts

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