router

package
Version: v0.12.1 Latest Latest
Warning

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

Go to latest
Published: Jul 26, 2021 License: BSD-3-Clause, MIT, MIT-0, MIT Imports: 5 Imported by: 0

README

router - general purpose (HTTP, etc.) router

go get -u "tawesoft.co.uk/go"
import "tawesoft.co.uk/go/router"
Links License Stable?
homedocssrc MIT candidate

About

Package router is a general purpose router of methods (e.g. HTTP "GET") and paths (e.g. "/user/123/profile") to some value e.g. a controller.

Supports named routes, route parameters, constructing a path from a route, pattern submatches, etc.

Although built with HTTP routing in mind, this is a general purpose implementation that can route to any type of value - it is not limited to HTTP handlers.

Examples

Demonstrates simple HTTP routing and named routes with a server at localhost:8080

package main

import (
    "fmt"
    "html/template"
    "log"
    "net/http"
    "regexp"
    "strconv"
    
    "tawesoft.co.uk/go/router"
)

type User struct {
    Name string
}

const Me = 1

var Users = map[int]User{
    0: {"Alan Turing"},
    1: {"Grace Hopper"},
    2: {"Donald Knuth"},
}

func HandleIndex(w http.ResponseWriter, r *http.Request, match *router.Match) {
    const tpl = `
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Home</title>
    </head>
    <body>
        <h1>Home</h1>
        <a href="/users">Users</a>
    </body>
</html>`
    fmt.Fprintf(w, tpl)
}

func HandleUsersIndex(w http.ResponseWriter, r *http.Request, match *router.Match) {
    const tpl = `
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Users</title>
    </head>
    <body>
        <h1>Users</h1>
        <div><a href="{{ UserPath -1 }}"><b>My Profile</b></a></div>
        {{range $i, $u := . }}
            <div><a href="{{ UserPath $i }}">{{ $u.Name }}</a></div>
        {{else}}
            <div><strong>no rows</strong></div>
        {{end}}
    </body>
</html>`
    
    t, err := template.New("webpage").Funcs(template.FuncMap{
        // use a named route to construct the URL for a user's profile
        // to avoid hardcoding a URL
        "UserPath": func(i int) string {
            if i < 0 {
                return match.Router.MustFormat(match.Router.MustNamed("My Profile"))
            }
            return match.Router.MustFormat(match.Router.MustNamed("User Profile"), strconv.Itoa(i))
        },
    }).Parse(tpl)
    if err != nil { panic(err) }
    
    err = t.Execute(w, Users)
    if err != nil { panic(err) }
}

func HandleUserMe(w http.ResponseWriter, r *http.Request, match *router.Match) {
    fmt.Fprintf(w, "I am %s", Users[Me].Name)
}

func HandleUserById(w http.ResponseWriter, r *http.Request, match *router.Match) {
    id, err := strconv.Atoi(match.Value("id"))
    if err != nil { panic(err) }
    fmt.Fprintf(w, "I am %s", Users[id].Name)
}

type MyHandlerType func (http.ResponseWriter, *http.Request, *router.Match)

func main() {
    id := regexp.MustCompile(`^\d{1,9}$`)
    
    type MyHandler struct {
        handle MyHandlerType
    }
    
    routes := router.Route{Name: "Root", Children: []router.Route{
        {Name: "Home", Methods: "GET", Handler: MyHandler{HandleIndex}},
        {Name: "Users", Pattern: "users", Methods: "GET, POST", Handler: MyHandler{HandleUsersIndex}, Children: []router.Route{
            {Pattern: "me", Methods: "GET", Name: "My Profile", Handler: MyHandler{HandleUserMe}},
            {Pattern: id, Key: "id", Methods: "GET", Name: "User Profile", Handler: MyHandler{HandleUserById}},
        }},
    }}
    
    router, err := router.New(routes)
    if err != nil { panic(err) }
    
    log.Fatal(http.ListenAndServe(":8080", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
        match := router.MatchHttpRequest(r)
        
        // how you handle the match is up to you!
        if match != nil {
            match.Route.Handler.(MyHandler).handle(w, r, match)
        } else {
            http.NotFound(w, r)
            fmt.Fprintf(w, "Not Found: Sorry, no matching route!")
        }
    })))
}

Getting Help

This package is part of tawesoft.co.uk/go, a monorepo for small Go modules maintained by Tawesoft®. Check out that URL for more information about other Go modules from Tawesoft plus community and commercial support options.

Documentation

Overview

Package router is a general purpose router of methods (e.g. HTTP "GET") and paths (e.g. "/user/123/profile") to some value e.g. a controller.

Supports named routes, route parameters, constructing a path from a route, pattern submatches, etc.

Although built with HTTP routing in mind, this is a general purpose implementation that can route to any type of value - it is not limited to HTTP handlers.

Examples

Demonstrates simple HTTP routing and named routes with a server at localhost:8080

https://www.tawesoft.co.uk/go/doc/router/examples/httprouting/

Package Information

License: MIT (see LICENSE.txt)

Stable: candidate

For more information, documentation, source code, examples, support, links, etc. please see https://www.tawesoft.co.uk/go and https://www.tawesoft.co.uk/go/router

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Match

type Match struct {
	// Route is the route picked - never nil
	Route *Route

	// Router is the router that was matched on
	Router *Router
	// contains filtered or unexported fields
}

Match is the result of a routing query - may be nil if no match found

func (Match) Submatch

func (match Match) Submatch(key string, index int) string

Submatch returns a submatch of a parsed path parameter identified by a Route Key and

For example, you might create a tree of routes such that the path "/user/123.json" captures the path component "123.json" and associates it with a "user-id" route.Key. For a pattern like "(\d+)(\.(\w+))?", the submatches for that key would be ["123.json", "123", ".json", "json"], so index 1 gives "123".

func (Match) Submatches

func (match Match) Submatches() map[string][]string

Submatches returns a non-nil mapping of the route keys => submatches of path parameters.

func (Match) Value

func (match Match) Value(key string) string

Value returns a parsed path parameter identified by a Route Key.

For example, you might create a tree of routes such that the path "/user/123/profile" captures the path component "123" and associates it with a "user-id" route.Key.

func (Match) Values

func (match Match) Values() map[string]string

Values returns a non-nil mapping of the route keys => path parameters

type Route

type Route struct {
	// Name (optional) is a way of uniquely identifying a specific route. The
	// name must be unique to the whole router tree.
	Name string

	// Key (optional) is a way of uniquely identifying a path parameter (e.g.
	// the path "/user/123/profile" might have a parameter for the user ID).
	//
	// This key is used to query the value of the path parameter by name in a
	// Match result.
	//
	// All keys must be unique to a route and its parents, but does not have to
	// be unique across the entire router tree.
	Key string

	// Pattern (optional) is a way of matching against a path component. This
	// may be a literal string e.g. "user", or or a compiled regular expression
	// (https://golang.org/pkg/regexp/) such as regexp.MustCompile(`$\d{1,5}^`)
	// (this matches one to five ASCII digits).
	//
	// Note that the regexp implementation provided by regexp is guaranteed to
	// run in time linear in the size of the input so it is generally not
	// essential to limit the length of the path component. Path components are
	// limited to 255 characters regardless.
	//
	// If the pattern is nil or an empty string, the route matches an empty
	// path component. This is useful for the root node, or index pages for
	// folders.
	//
	// As a special case, the string "*" matches everything. This is useful for
	// a per-directory catch-all.
	//
	// Patterns are matched in the following order first, then in order of
	// sequence defined:
	//
	// 1 Empty path (Pattern is nil or empty string; Final has no effect)
	// 2 Final Exact matches (Pattern is string and Final is true)
	// 3 Exact matches (Pattern is string and Final is false)
	// 4 Final Regex matches (Pattern is Regexp and Final is true)
	// 5 Regex matches (Pattern is Regexp and Final is false)
	// 6 Wildcard (string "*" and Final is false)
	// 7 Final Wildcard (string "*" and Final is true)
	Pattern interface{}

	// Methods (optional) is a comma-separated string of methods or verbs
	// accepted by the route e.g. HTTP "GET, POST". If left empty, implies
	// only the Router's DefaultMethods.
	//
	// Note that "OPTIONS" is not handled automatically - this is up to you.
	Methods string

	// Handler (optional) is the caller-supplied information attached to a
	// route e.g. a HTTP Handler or any custom value.
	//
	// It is up to the caller to do something with the resulting Handler -
	// nothing is called automatically for you.
	Handler interface{}

	// Children (optional) are any child Routes for subsequent path components.
	// For example, the path "/foo/bar" decomposes into the path components
	// ["", "foo", "bar"], and can be matched by a route with pattern "",
	// child route with pattern "foo", and grandchild route "bar".
	Children []Route

	// Final (optional; default false), if true, indicates that the pattern
	// should be matched against the entire remaining path, not just the
	// current path component.
	//
	// For example, a Route might have a pattern to accept arbitrary
	// files in subdirectories, like "static/assets/foo.png". It would be
	// Final with a Pattern value of something like
	// regexp.MustCompile(`(\w+/)*\w+\.\w`)
	Final bool
}

Route describes a mapping between methods (like HTTP "GET") and path component (like "foo" in "/foo/bar") to a user-supplied handler value.

Each route may contain child routes which map to subsequent path components. For example a route may match "foo" in "/foo/bar", and a child route could match "bar".

A path such as "/user/123/profile" resolves into the components ["", "user", "123", "profile"]. A path such as "/user/123/profile/" resolves into the components ["", "user", "123", "profile", ""]. As such, the root Route and folder-index Routes will normally be configured to match the empty string "".

A route can be Final, in which case it matches all remaining path components at once.

type Router

type Router struct {
	// DefaultMethods is the comma-separated list of methods to use if a route
	// has none specified e.g. HTTP "GET, POST".
	//
	// router.New() sets this to only "GET" by default.
	DefaultMethods string
	// contains filtered or unexported fields
}

Router is a general purpose router of a method or verb (like HTTP "GET") and path (like "/foo/bar") to a tree of Route objects.

func New

func New(root Route) (*Router, error)

Creates a new router with DefaultMethods set to GET

func (*Router) Format

func (router *Router) Format(route *Route, args ...string) (string, error)

Format creates a URL for a route - the opposite of routing. Any regexp.Regexp patterns are replaced using each arg in sequence.

WARNING: The return value from this function may be controlled by the User Agent. Escape it as necessary.

func (*Router) FormatMap

func (router *Router) FormatMap(route *Route, mapping map[string]string) (string, error)

Format creates a URL for a route - the opposite of routing. Any regexp.Regexp patterns are replaced using each the route Key to lookup a value in the the mapping argument. Pass in Match Values() to have this automatically filled based on the parsed request.

WARNING: The return value from this function may be controlled by the User Agent. Escape it as necessary.

func (*Router) Match

func (router *Router) Match(method string, path string) *Match

Match attempts to match a method (e.g. a HTTP method like "GET") and path (e.g. "/foo/bar") to a route in a router's tree of routes. In the event that there is no match, returns nil.

func (*Router) MatchHttpRequest

func (router *Router) MatchHttpRequest(r *http.Request) *Match

Matches a HTTP request to a route. See Match

func (*Router) MustFormat

func (router *Router) MustFormat(route *Route, args ...string) string

MustFormat is like Format, but panics on error.

func (*Router) MustFormatMap

func (router *Router) MustFormatMap(route *Route, mapping map[string]string) string

MustFormatMap is like FormatMap, but panics on error.

func (*Router) MustNamed

func (router *Router) MustNamed(name string) *Route

MustNamed is like Named, but panics if not found.

func (*Router) Named

func (router *Router) Named(name string) *Route

Named returns a named Route where `name == route.Name`. Nil if not found.

func (*Router) Parent

func (router *Router) Parent(route *Route) *Route

Parent returns the parent Route of a Route in the router's tree of Routes. May be nil (i.e. at the root).

Directories

Path Synopsis
examples
submatches
Demonstrates HTTP routing with "submatch" patterns in a path component with a server at localhost:8080
Demonstrates HTTP routing with "submatch" patterns in a path component with a server at localhost:8080

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
t or T : Toggle theme light dark auto
y or Y : Canonical URL