fastrouter

package module
Version: v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2017 License: BSD-3-Clause Imports: 5 Imported by: 3

README

FastRouter

FastRouter is a fast, flexible HTTP router written in Go.

FastRouter contains some customizable options, such as TrailingSlashesPolicy, PanicHandler, OptionsHandler, MethodNotAllowedHandler, NotFoundHandler and so on.

FastRouter also provides some useful features, such as grouping and middleware.

GoDoc Build Status Go Report Card Coverage Status

Features

Fast: See Go Web Framework Benchmark

Flexible: FastRouter provides some customizable options for you:

  • TrailingSlashesPolicy:
    • IgnoreTrailingSlashes: ignore trailing slashes.
    • AppendTrailingSlashes: append trailing slashes and redirect if request path is not end with '/'.
    • RemoveTrailingSlashes: remove trailing slashes and redirect if request path is end with '/'.
    • StrictTrailingSlashes: remove or append trailing slashes according to corresponding pattern.
  • PanicHandler
  • OptionsHandler
  • MethodNotAllowedHandler
  • NotFoundHandler

Compatible: FastRouter is an implementation of http.Handler, so it is compatible with third-party packages.

Middleware: Middleware is a chaining tool for chaining http.Handler, see Middleware.

Grouping: Grouping is an useful feature of FastRouter, it allows to nest and specify middleware of group, see Grouping.

Documentation

See Documentation for details.

Examples

See Examples for details.

Documentation

Overview

Package fastrouter is a fast, flexible HTTP router written in Go.

FastRouter exports options to custom router, such as 'TrailingSlashesPolicy', 'PanicHandler', 'OptionsHandler', 'MethodNotAllowedHandler', 'NotFoundHandler' and so on.

FastRouter also provides some useful features, such as grouping and middleware.

Example
package main

import (
	"log"
	"net/http"
	"strings"

	"github.com/razonyang/fastrouter"
)

func main() {
	r := fastrouter.New()

	// TrailingSlashesPolicy
	r.TrailingSlashesPolicy = fastrouter.StrictTrailingSlashes
	// PanicHandler
	r.PanicHandler = func(w http.ResponseWriter, req *http.Request, rcv interface{}) {
		log.Printf("received a panic: %#v\n", rcv)
	}
	// OptionsHandler
	r.OptionsHandler = func(w http.ResponseWriter, req *http.Request, methods []string) {
		w.Header().Set("Allow", strings.Join(methods, ", "))
		w.Write([]byte("user-defined OptionsHandler"))
	}
	// MethodNotAllowedHandler
	r.MethodNotAllowedHandler = func(w http.ResponseWriter, req *http.Request, methods []string) {
		w.WriteHeader(http.StatusMethodNotAllowed)
		w.Header().Set("Allow", strings.Join(methods, ", "))
		w.Write([]byte("user-defined MethodNotAllowedHandler"))
	}
	// NotFoundHandler
	r.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("user-defined NotFoundHandler"))
	})

	// homepage handler
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello world"))
	})
	// panic handler
	r.Get("/panic", func(w http.ResponseWriter, r *http.Request) {
		panic("panic handler")
	})
	// hello handler
	r.Get("/hello/<name>", func(w http.ResponseWriter, r *http.Request) {
		var name string

		name = fastrouter.Params(r)["name"]
		/* The following code snippet is equivalent.
		if params, ok := r.Context().Value(fastrouter.ParamsKey{}).(map[string]string); ok {
			name = params["name"]
		}
		*/

		w.Write([]byte("hello " + name))
	})

	// RESTful API
	r.Get("/users", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("query users"))
	})
	r.Post("/users", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("create an user"))
	})
	r.Get("/users/<name>", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(fastrouter.Params(r)["name"] + " profile"))
	})
	r.Delete("/users/<name>", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("deleted user " + fastrouter.Params(r)["name"]))
	})
	r.Put("/users/<name>", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("updated " + fastrouter.Params(r)["name"] + " profile"))
	})
	r.Get("/users/<name>/posts", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("the posts created by " + fastrouter.Params(r)["name"]))
	})

	// Make preparations before handling incoming request.
	// Note that, this method MUST be invoked before handling incoming request,
	// otherwise the router can not works as expected.
	r.Prepare()

	log.Fatal(http.ListenAndServe(":8080", r))
}
Output:

Index

Examples

Constants

View Source
const (
	// ignore trailing slashes.
	IgnoreTrailingSlashes = iota

	// append trailing slashes and redirect if request path is not end with '/'.
	AppendTrailingSlashes

	// remove trailing slashes and redirect if request path is end with '/'.
	RemoveTrailingSlashes

	// remove or append trailing slashes according to corresponding pattern.
	StrictTrailingSlashes
)

Trailing slashes policies.

Variables

This section is empty.

Functions

func Params

func Params(r *http.Request) map[string]string

Params returns the parameters of the request path.

Types

type Middleware

type Middleware func(next http.Handler) http.Handler

Middleware is a chaining tool for chaining http.Handler.

Handler workflow:

 Root Router Middleware
          ↓
Group Router Middleware
          ↓
   Handler Middleware
          ↓
        Handler
Example
package main

import (
	"log"
	"net/http"

	"github.com/razonyang/fastrouter"
)

func main() {
	// basic auth middleware
	basicAuthMiddleware := func(next http.Handler) http.Handler {
		username := "foo"
		password := "bar"

		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if name, passwd, ok := r.BasicAuth(); ok && name == username && passwd == password {
				// authorized, continue processing
				next.ServeHTTP(w, r)
				return
			}

			w.WriteHeader(http.StatusUnauthorized)
		})
	}

	// body limit middleware
	bodyLimitMiddleware := func(next http.Handler) http.Handler {
		var limit int64 = 1024

		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.ContentLength > limit {
				w.WriteHeader(http.StatusRequestEntityTooLarge)
				return
			}

			// continue processing
			next.ServeHTTP(w, r)
		})
	}

	r := fastrouter.New()

	// set basic auth middleware as global middleware.
	r.Middleware = append(r.Middleware, basicAuthMiddleware)

	postCreate := func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Created"))
	}
	r.Post(`/posts`, postCreate)

	// set body limit middleware for upload handler.
	upload := func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Uploaded"))
	}
	r.Post(`/upload`, upload, bodyLimitMiddleware)

	// Make preparations before handling incoming request.
	// Note that, this method MUST be invoked before handling incoming request,
	// otherwise the router can not works as expected.
	r.Prepare()

	log.Fatal(http.ListenAndServe(":8080", r))
}
Output:

type ParamsKey

type ParamsKey struct{}

ParamsKey is an empty struct, it is the second parameter of context.WithValue for storing the request parameters.

type Parser

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

Parser is the default pattern parser which implements ParserInterface.

func NewParser

func NewParser() Parser

NewParser returns a new parser via NewParserWithReg with the defaultParserRegexp.

func NewParserWithReg

func NewParserWithReg(reg *regexp.Regexp) Parser

NewParserWithReg returns a new parser with the given regexp.

func (Parser) Parse

func (p Parser) Parse(pattern string) (regexp string, params []string, hasTrailingSlashes bool, err error)

Parse implements ParserInterface's Parse method.

The pattern MUST be begin with '/', the pattern parse rule is related regexp, by default defaultParserRegexp is used, you can also define your own parse rule via NewParserWithReg.

The following introduction and examples is about of defaultParserRegexp.

The pattern can be divided into two types:

1. Without named parameter:

"/"
"/users"
...

2. With named parameter:

`/users/<name>`
`/users/<name>/posts`
`/posts/<year:\d{4}>/<month:\d{2}>/<title>`
...

Named parameter MUST be one of '<name>' and '<name:regexp>'.

`<name>`        // will be converted to `([^/]+)`

`<name:regexp>` // will be converted to `(regexp)`

Examples:

| Pattern                                     | Error   | Regexp                             | hasTrailingSlashes | Params                               |
|:--------------------------------------------|:--------|:-----------------------------------|:-------------------|:-------------------------------------|
|                                             | non-nil |                                    |                    |                                      |
| `no-start-with-slashes`                     | non-nil |                                    |                    |                                      |
| `/`                                         | nil     | `//?`                              | NO                 |                                      |
| `/hello/<name>`                             | nil     | `/hello/([^/]+)/?`                 | NO                 | `[]string{"name"}`                   |
| `/users`                                    | nil     | `/users/?`                         | NO                 |                                      |
| `/users/<name:\w+>`                         | nil     | `/users/(\w+)/?`                   | NO                 | `[]string{"name"}`                   |
| `/users/<name:\w+>/posts/`                  | nil     | `/users/(\w+)/posts/?`             | YES                | `[]string{"name"}`                   |
| `/orders/<id:\d+>`                          | nil     | `/orders/(\d+)/?`                  | NO                 | `[]string{"id"}`                     |
| `/posts/<year:\d{4}>/<month:\d{2}>/<title>` | nil     | `/posts/(\d{4})/(\d{2})/([^/]+)/?` | NO                 | `[]string{"year", "month", "title"}` |

type ParserInterface

type ParserInterface interface {
	// Parse extracts information from pattern.
	//
	// The pattern will be parsed by parser, parse rule is related to parser.
	//
	// The regexp MUST be a valid regular expression string for
	// indicating which request paths can be matched.
	//
	// The params is a slice that contains pattern named parameters,
	// in order.
	//
	// The hasTrailingSlashes indicate that whether pattern has
	// trailing slashes, this flag has effect on strict trailing
	// slashes policy.
	//
	// Returns non-nil error, if parsing failed.
	Parse(pattern string) (regexp string, params []string, hasTrailingSlashes bool, err error)
}

ParserInterface defines a Parse method for parsing pattern.

type Router

type Router struct {

	// Middleware.
	Middleware []Middleware

	// The handler for handling panic.
	//
	// The rcv contains panic information, rcv = recover().
	//
	// This options is only effective in root router.
	PanicHandler func(w http.ResponseWriter, req *http.Request, rcv interface{})

	// The handler for handling OPTIONS request.
	//
	// The methods contains all allowed methods of the request path.
	//
	// This options is only effective in root router.
	OptionsHandler func(w http.ResponseWriter, req *http.Request, methods []string)

	// The handler for handling Method Not Allowed.
	//
	// The methods contains all allowed methods of the request path.
	//
	// This options is only effective in root router.
	MethodNotAllowedHandler func(w http.ResponseWriter, req *http.Request, methods []string)

	// The handler for handling Not Found.
	//
	// This options is only effective in root router.
	NotFoundHandler http.Handler

	// Trailing slashes policy:
	//     IgnoreTrailingSlashes, by default
	//     AppendTrailingSlashes
	//     RemoveTrailingSlashes
	//     StrictTrailingSlashes
	//
	// This options is only effective in root router.
	TrailingSlashesPolicy int8
	// contains filtered or unexported fields
}

Router is an implementation of http.Handler for handling HTTP requests.

func New

func New() *Router

New returns a new Router with the default parser via NewWithParser.

func NewWithParser

func NewWithParser(parser ParserInterface) *Router

NewWithParser returns a new Router with the given parser.

func (*Router) Delete

func (r *Router) Delete(pattern string, handler http.HandlerFunc, middleware ...Middleware)

Delete is a shortcut of Handle for handling DELETE request.

func (*Router) Get

func (r *Router) Get(pattern string, handler http.HandlerFunc, middleware ...Middleware)

Get is a shortcut of Handle for handling GET request.

func (*Router) Group

func (r *Router) Group(prefix string) *Router

Group returns a new group router with then given prefix.

Example
package main

import (
	"log"
	"net/http"

	"github.com/razonyang/fastrouter"
)

func main() {
	r := fastrouter.New()
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("root"))
	})

	// group frontend
	frontend := r.Group("frontend")
	frontend.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("frontend"))
	})
	// nested group
	frontendUser := frontend.Group("user")
	frontendUser.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("frontend user"))
	})

	// group backend
	backend := r.Group("backend")
	backend.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("backend"))
	})

	// Make preparations before handling incoming request.
	// Note that, this method MUST be invoked before handling incoming request,
	// otherwise the router can not works as expected.
	r.Prepare()

	log.Fatal(http.ListenAndServe(":8080", r))
}
Output:

func (*Router) Handle

func (r *Router) Handle(method, pattern string, handler http.HandlerFunc, middleware ...Middleware)

Handle registers handler with the given method, pattern and middleware.

The request method is case sensitive.

The handler is a http.HandlerFunc that handle request.

It also allows to specify middleware for the given handler, for example, we usually specify a body limit middleware for the upload handler.

Causes a panic if parsing failed, such as invalid pattern.

func (*Router) Post

func (r *Router) Post(pattern string, handler http.HandlerFunc, middleware ...Middleware)

Post is a shortcut of Handle for handling POST request.

func (*Router) Prepare

func (r *Router) Prepare()

Prepare makes preparations before handling requests:

1. combines route's regular expressions;

2. chaining middleware.

Note that, router MUST makes preparations before handling request, otherwise it can not works as expected.

func (*Router) Put

func (r *Router) Put(pattern string, handler http.HandlerFunc, middleware ...Middleware)

Put is a shortcut of Handle for handling PUT request.

func (*Router) ServeFiles

func (r *Router) ServeFiles(pattern, root string, middleware ...Middleware)

ServeFiles serve static resources.

The pattern MUST contains parameter placeholder named "filepath", it is related to pattern parser.

The root is the absolute or relative path of the static resources.

Example
package main

import (
	"log"
	"net/http"

	"github.com/razonyang/fastrouter"
)

func main() {
	r := fastrouter.New()

	// matched the root directory resources,
	// subdirectory resources can not be matched.
	r.ServeFiles("/css/<filepath>", "/path-to-css/")

	// both of root directory and subdirectory resources
	// cab be matched.
	r.ServeFiles("/js/<filepath:.+>", "/path-to-js/")

	// Make preparations before handling incoming request.
	// Note that, this method MUST be invoked before handling incoming request,
	// otherwise the router can not works as expected.
	r.Prepare()

	log.Fatal(http.ListenAndServe(":8080", r))
}
Output:

func (*Router) ServeHTTP

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

ServeHTTP implements http.Handler's ServeHTTP method.

Jump to

Keyboard shortcuts

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