apirouter

package module
v0.0.0-...-1a234f7 Latest Latest
Warning

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

Go to latest
Published: Jan 26, 2021 License: MIT Imports: 10 Imported by: 1

README

ApiRouter

中文

apirouter provides a lightning fast RESTful api router.

Motivation

When developing server-side applications, it is necessary to provide RESTful APIs. I would like to have a library:

  • Simple: focus on path parameter extraction and routing
  • Fast: better performance

aero performance is good, but it's more like a framwork. httprouter functions are focusing, but its performance is mediocre.

I had to write one myself as an exercise

Features

  • Best Performance: Benchmarks speak for themselves
  • Compatibility with the http.Handler interface
  • Named parameters, regular expressions parameters and wildcard parameters
  • Support gRPC RESTful api style
  • Smart prioritized routes
  • No allocations, matching and retrieve parameters don't allocates.

Installing

  1. Get package:

    go get -u github.com/cnotch/apirouter
    
  2. Import it in your code:

    import "github.com/cnotch/apirouter"
    

Usage

It is recommended to work with http.ServeMux to take advantage of the standard library.

  1. Make a new router:

    r := apirouter.New(
    	apirouter.API("GET","/",func(w http.ResponseWriter, r *http.Request, ps apirouter.Params){
    		fmt.Fprint(w, "Hello")
    	}),
    )
    

    Remember that HTTP methods are case-sensitive and uppercase by convention (RFC 7231 4.1).
    So you can directly use the built-in shortcuts for standard HTTP methods, such as apirouter.GET...

  2. Give the router to the server:

    http.ListenAndServe(":8080", r)
    
Precedence example

On the example below the router will test the routes in the following order, /users/list then /users/:id=^\d+$ then /users/:id then /users/*page.

r:= apirouter.New(
	apirouter.GET("/users/:id",...),
	apirouter.GET(`/users/:id=^\d+$`,...),
	apirouter.GET("/users/*page",...),
	apirouter.GET("/users/list",...),
)
Pattern Styles
Default style

On the example below the router will use default style.

r:= apirouter.New(
	apirouter.API("GET", `/user/:id=^\d+$/books`,h),
	apirouter.API("GET", "/user/:id",h),
	apirouter.API("GET", "/user/:id/profile/:theme",h),
	apirouter.API("GET", "/images/*file",h),
)

Default sytle syntax:

Pattern		= "/" Segments
Segments	= Segment { "/" Segment }
Segment		= LITERAL | Parameter
Parameter	= Anonymous | Named
Anonymous	= ":" | "*"
Named		= ":" FieldPath [ "=" Regexp ] | "*" FieldPath
FieldPath	= IDENT { "." IDENT }
gRPC style

On the example below the router will use gRPC style.

r:= apirouter.NewForGRPC(
	apirouter.API("GET", `/user/{id=^\d+$}/books`,h),
	apirouter.API("GET", "/user/{id}",h),
	apirouter.API("GET", "/user/{id}:verb",h),
	apirouter.API("GET", "/user/{id}/profile/{theme}",h),
	apirouter.API("GET", "/images/{file=**}",h),
	apirouter.API("GET", "/images/{file=**}:jpg",h),
)

gRPC sytle syntax:

Pattern		= "/" Segments [ Verb ] ;
Segments	= Segment { "/" Segment } ;
Segment		= LITERAL | Parameter
Parameter	= Anonymous | Named
Anonymous	= "*" | "**"
Named		= "{" FieldPath [ "=" Wildcard ] "}"
Wildcard	= "*" | "**" | Regexp
FieldPath	= IDENT { "." IDENT } ;
Verb		= ":" LITERAL ;
Parameters

The value of parameters is saved as a Params. The Params is passed to the Handler func as a third parameter.

If the handler is registered with Handle or HandleFunc, it is stored in request's context and can be accessed by apirouter.PathParams.

Named

A named parameter begins with : and matches any value until the next / or end of path.

Example, with a parameter id:

r:=apirouter.New(
	apirouter.GET("/streams/:id", func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		id := ps.ByName("id")
		fmt.Fprintf(w, "Page of stream #%s", id)
	}),

	apirouter.HandleFunc("GET","/users/:id", func(w http.ResponseWriter, r *http.Request) {
		ps := apirouter.PathParams(r.Context())
		id := ps.ByName("id")
		fmt.Fprintf(w, "Page of user #%s", id)
	}),
)

If you don't need to retrieve the parameter value by name, you can omit the parameter name:

r:=apirouter.New(
	apirouter.GET(`/users/:`, func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		id := ps.Value(0)
		fmt.Fprintf(w, "Page of user #%s", id)
	}),
)

A parameter can be used on the same level as a static route, without conflict:

r:=apirouter.New(
	apirouter.GET("/users/admin", func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		fmt.Fprint(w, "admin page")
	}),

	apirouter.GET("/users/:id", func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		id := ps.ByName("id")
		fmt.Fprintf(w, "Page of user #%s", id)
	}),
)
Regular expressions

If a parameter must match an exact pattern (digits only, for example), you can also set a regular expression constraint just after the parameter name and =:

r:=apirouter.New(
	apirouter.GET(`/users/:id=^\d+$`, func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		id := ps.ByName("id")
		fmt.Fprintf(w, "Page of user #%s", id)
	}),
)

NOTE: No more than 256 different regular expressions are allowed.

WARN: Regular expressions can significantly reduce performance.

A parameter with a regular expression can be used on the same level as a simple parameter, without conflict:

r:=apirouter.New(
	apirouter.GET("/users/admin", func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		fmt.Fprint(w, "admin page")
	}),

	apirouter.GET(`/users/:id=^\d+$`, func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		id := ps.ByName("id")
		fmt.Fprintf(w, "Page of user #%s", id)
	}),

	apirouter.GET("/users/:id", func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		id := ps.ByName("id")
		fmt.Fprintf(w, "Page of user #%s", id)
	}),
)
Wildcard

Wildcard parameters match anything until the path end, not including the directory index (the '/' before the '*'). Since they match anything until the end, wildcard parameters must always be the final path element.

The rest of the request path becomes the parameter value of *:

r:=apirouter.New(
	apirouter.GET("/files/*filepath", func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		filepath := ps.ByName("filepath")
		fmt.Fprintf(w, "Get file %s", filepath)
	}),
)

Deeper route paths with the same prefix as the wildcard will take precedence, without conflict:

// Will match:
// 	/files/one
// 	/files/two
// 	...
r:=apirouter.New(
	apirouter.GET("/files/:name", func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		name := ps.ByName("name")
		fmt.Fprintf(w, "Get root file #%s", name)
	}),
)

// Will match:
// 	/files/one/...
// 	/files/two/...
// 	...
r:=apirouter.New(
	apirouter.GET("/files/*filepath", func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		filepath := ps.ByName("filepath")
		fmt.Fprintf(w, "Get file %s", filepath)
	}),
)

// Will match:
// 	/files/movies/one
// 	/files/movies/two
// 	...
r:=apirouter.New(
	apirouter.GET("/files/movies/:name", func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		name := ps.ByName("name")
		fmt.Fprintf(w, "Get movie #%s", name)
	}),
)
Static files

For serving static files, like for the standard net/http.ServeMux, just bring your own handler.

Example, with the standard net/http.FileServer:

r:=apirouter.New(
	apirouter.Handle("GET","/static/*filepath", 
		http.StripPrefix("/static/", http.FileServer(http.Dir("static")))),
)
Custom "not found" handler

When a request match no route, the response status is set to 404 and an empty body is sent by default.

But you can set your own "not found" handler.
In this case, it's up to you to set the response status code (normally 404):

r:=apirouter.New(
	apirouter.NotFoundHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.NotFound(w, r)
	})),
)
Work with "http.Handler"

You can use Handle and HandleFunc to register the Handler for the standard library(http.Handler or http.HandlerFunc)

NOTE: Since the Handler using the standard library needs to add a new context to the Request, performance can suffer.

Benchmarks

Environment
goos: darwin
goarch: amd64
pkg: github.com/julienschmidt/go-http-routing-benchmark
Single Route
BenchmarkAero_Param               	23004813	        52.7 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_Param          	26834293	        44.4 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_Param              	  908820	      1292 ns/op	     352 B/op	       3 allocs/op
BenchmarkGin_Param                	13086147	        86.9 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_Param         	  458750	      2508 ns/op	    1280 B/op	      10 allocs/op
BenchmarkGowwwRouter_Param        	 1760046	       669 ns/op	     432 B/op	       3 allocs/op
BenchmarkHttpRouter_Param         	10678444	       106 ns/op	      32 B/op	       1 allocs/op

BenchmarkAero_Param5              	15816512	        78.6 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_Param5         	18431535	        64.9 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_Param5             	  771405	      1506 ns/op	     352 B/op	       3 allocs/op
BenchmarkGin_Param5               	 7365034	       155 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_Param5        	  323251	      3662 ns/op	    1344 B/op	      10 allocs/op
BenchmarkGowwwRouter_Param5       	 1602684	       749 ns/op	     432 B/op	       3 allocs/op
BenchmarkHttpRouter_Param5        	 3771800	       313 ns/op	     160 B/op	       1 allocs/op

BenchmarkAero_Param20             	32261661	        36.9 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_Param20        	 7118907	       167 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_Param20            	  375331	      3003 ns/op	     352 B/op	       3 allocs/op
BenchmarkGin_Param20              	 2895128	       418 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_Param20       	  137173	      8534 ns/op	    3451 B/op	      12 allocs/op
BenchmarkGowwwRouter_Param20      	 1000000	      1073 ns/op	     432 B/op	       3 allocs/op
BenchmarkHttpRouter_Param20       	 1000000	      1041 ns/op	     640 B/op	       1 allocs/op

BenchmarkAero_ParamWrite          	13136160	        91.3 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_ParamWrite     	13807000	        87.0 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_ParamWrite         	  866906	      1367 ns/op	     360 B/op	       4 allocs/op
BenchmarkGin_ParamWrite           	 8124031	       149 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_ParamWrite    	  436358	      2580 ns/op	    1280 B/op	      10 allocs/op
BenchmarkGowwwRouter_ParamWrite   	  576049	      1839 ns/op	     976 B/op	       8 allocs/op
BenchmarkHttpRouter_ParamWrite    	 8232554	       140 ns/op	      32 B/op	       1 allocs/op
GithubAPI Routes: 203
   Aero: 472856 Bytes
   ApiRouter: 93488 Bytes
   Beego: 150936 Bytes
   Gin: 58512 Bytes
   GorillaMux: 1322784 Bytes
   GowwwRouter: 80008 Bytes
   HttpRouter: 37096 Bytes

BenchmarkAero_GithubStatic        	22771987	        52.7 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_GithubStatic   	39730426	        30.9 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_GithubStatic       	  981472	      1285 ns/op	     352 B/op	       3 allocs/op
BenchmarkGin_GithubStatic         	11793066	       104 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_GithubStatic  	  216598	      5578 ns/op	     976 B/op	       9 allocs/op
BenchmarkGowwwRouter_GithubStatic 	15343174	        77.9 ns/op	       0 B/op	       0 allocs/op
BenchmarkHttpRouter_GithubStatic  	24002008	        43.5 ns/op	       0 B/op	       0 allocs/op

BenchmarkAero_GithubParam         	10279070	       115 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_GithubParam    	14197182	        81.4 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_GithubParam        	  760636	      1438 ns/op	     352 B/op	       3 allocs/op
BenchmarkGin_GithubParam          	 6255403	       191 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_GithubParam   	  143298	      8273 ns/op	    1296 B/op	      10 allocs/op
BenchmarkGowwwRouter_GithubParam  	 1589114	       762 ns/op	     432 B/op	       3 allocs/op
BenchmarkHttpRouter_GithubParam   	 4567689	       265 ns/op	      96 B/op	       1 allocs/op

BenchmarkAero_GithubAll           	   52720	     22547 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_GithubAll      	   68655	     17286 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_GithubAll          	    4044	    302730 ns/op	   71457 B/op	     609 allocs/op
BenchmarkGin_GithubAll            	   27684	     43329 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_GithubAll     	     294	   3991615 ns/op	  251654 B/op	    1994 allocs/op
BenchmarkGowwwRouter_GithubAll    	   10000	    157336 ns/op	   72144 B/op	     501 allocs/op
BenchmarkHttpRouter_GithubAll     	   23086	     53009 ns/op	   13792 B/op	     167 allocs/op
GPlusAPI Routes : 13
   Aero: 26840 Bytes
   ApiRouter: 32240 Bytes
   Beego: 10272 Bytes
   Gin: 4384 Bytes
   GorillaMux: 66208 Bytes
   GowwwRouter: 5744 Bytes
   HttpRouter: 2760 Bytes

BenchmarkAero_GPlusStatic         	29943988	        40.5 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_GPlusStatic    	53868162	        21.9 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_GPlusStatic        	  868545	      1224 ns/op	     352 B/op	       3 allocs/op
BenchmarkGin_GPlusStatic          	14917767	        77.6 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_GPlusStatic   	  634068	      1913 ns/op	     976 B/op	       9 allocs/op
BenchmarkGowwwRouter_GPlusStatic  	38727555	        31.8 ns/op	       0 B/op	       0 allocs/op
BenchmarkHttpRouter_GPlusStatic   	43723036	        26.8 ns/op	       0 B/op	       0 allocs/op

BenchmarkAero_GPlusParam          	17498322	        67.9 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_GPlusParam     	20866016	        58.5 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_GPlusParam         	  925665	      1356 ns/op	     352 B/op	       3 allocs/op
BenchmarkGin_GPlusParam           	 9070120	       132 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_GPlusParam    	  352827	      3254 ns/op	    1280 B/op	      10 allocs/op
BenchmarkGowwwRouter_GPlusParam   	 1751876	       686 ns/op	     432 B/op	       3 allocs/op
BenchmarkHttpRouter_GPlusParam    	 6812570	       172 ns/op	      64 B/op	       1 allocs/op

BenchmarkAero_GPlus2Params        	12382214	       101 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_GPlus2Params   	12620882	        93.6 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_GPlus2Params       	  826052	      1454 ns/op	     352 B/op	       3 allocs/op
BenchmarkGin_GPlus2Params         	 6700472	       176 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_GPlus2Params  	  198207	      6134 ns/op	    1296 B/op	      10 allocs/op
BenchmarkGowwwRouter_GPlus2Params 	 1663504	       730 ns/op	     432 B/op	       3 allocs/op
BenchmarkHttpRouter_GPlus2Params  	 5834750	       207 ns/op	      64 B/op	       1 allocs/op

BenchmarkAero_GPlusAll            	 1000000	      1008 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_GPlusAll       	 1336905	       900 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_GPlusAll           	   63999	     17971 ns/op	    4576 B/op	      39 allocs/op
BenchmarkGin_GPlusAll             	  672241	      1871 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_GPlusAll      	   22963	     51314 ns/op	   16112 B/op	     128 allocs/op
BenchmarkGowwwRouter_GPlusAll     	  139518	      8636 ns/op	    4752 B/op	      33 allocs/op
BenchmarkHttpRouter_GPlusAll      	  465562	      2384 ns/op	     640 B/op	      11 allocs/op
ParseAPI Routes: 26
   Aero: 29304 Bytes
   ApiRouter: 38928 Bytes
   Beego: 19280 Bytes
   Gin: 7776 Bytes
   GorillaMux: 105880 Bytes
   GowwwRouter: 9344 Bytes
   HttpRouter: 5024 Bytes

BenchmarkAero_ParseStatic         	25968115	        44.8 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_ParseStatic    	48030700	        26.1 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_ParseStatic        	  917887	      1268 ns/op	     352 B/op	       3 allocs/op
BenchmarkGin_ParseStatic          	14337064	        83.1 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_ParseStatic   	  560444	      2351 ns/op	     976 B/op	       9 allocs/op
BenchmarkGowwwRouter_ParseStatic  	36622987	        33.5 ns/op	       0 B/op	       0 allocs/op
BenchmarkHttpRouter_ParseStatic   	44521252	        28.2 ns/op	       0 B/op	       0 allocs/op

BenchmarkAero_ParseParam          	19545406	        61.8 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_ParseParam     	20230845	        62.7 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_ParseParam         	  892608	      1352 ns/op	     352 B/op	       3 allocs/op
BenchmarkGin_ParseParam           	12509785	        98.8 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_ParseParam    	  409419	      2631 ns/op	    1280 B/op	      10 allocs/op
BenchmarkGowwwRouter_ParseParam   	 1781226	       682 ns/op	     432 B/op	       3 allocs/op
BenchmarkHttpRouter_ParseParam    	 7534678	       157 ns/op	      64 B/op	       1 allocs/op

BenchmarkAero_Parse2Params        	16480020	        71.3 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_Parse2Params   	17407262	        69.9 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_Parse2Params       	  758266	      1398 ns/op	     352 B/op	       3 allocs/op
BenchmarkGin_Parse2Params         	 9756090	       123 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_Parse2Params  	  424528	      3090 ns/op	    1296 B/op	      10 allocs/op
BenchmarkGowwwRouter_Parse2Params 	 1712110	       691 ns/op	     432 B/op	       3 allocs/op
BenchmarkHttpRouter_Parse2Params  	 6507544	       185 ns/op	      64 B/op	       1 allocs/op

BenchmarkAero_ParseAll            	  689119	      1761 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_ParseAll       	  942591	      1302 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_ParseAll           	   35602	     33737 ns/op	    9152 B/op	      78 allocs/op
BenchmarkGin_ParseAll             	  364644	      3346 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_ParseAll      	   10000	    101224 ns/op	   30288 B/op	     250 allocs/op
BenchmarkGowwwRouter_ParseAll     	   87921	     13179 ns/op	    6912 B/op	      48 allocs/op
BenchmarkHttpRouter_ParseAll      	  406630	      2940 ns/op	     640 B/op	      16 allocs/op
Static Routes: 157
   Aero: 34824 Bytes
   ApiRouter: 29040 Bytes
   Beego: 98456 Bytes
   Gin: 34936 Bytes
   GorillaMux: 585632 Bytes
   GowwwRouter: 24968 Bytes
   HttpRouter: 21680 Bytes

BenchmarkAero_StaticAll           	  108292	     10464 ns/op	       0 B/op	       0 allocs/op
BenchmarkApiRouter_StaticAll      	  203727	      5969 ns/op	       0 B/op	       0 allocs/op
BenchmarkBeego_StaticAll          	    5949	    225294 ns/op	   55265 B/op	     471 allocs/op
BenchmarkGin_StaticAll            	   42675	     28789 ns/op	       0 B/op	       0 allocs/op
BenchmarkGorillaMux_StaticAll     	    1035	   1068733 ns/op	  153236 B/op	    1413 allocs/op
BenchmarkGowwwRouter_StaticAll    	   59713	     20066 ns/op	       0 B/op	       0 allocs/op
BenchmarkHttpRouter_StaticAll     	   96944	     12553 ns/op	       0 B/op	       0 allocs/op

Documentation

Overview

Package apirouter provides a lightning fast RESTful api router.

A trivial example is:

 package main

 import (
     "fmt"
     "github.com/cnotch/apirouter"
     "net/http"
     "log"
 )

 func Index(w http.ResponseWriter, r *http.Request, _ apirouter.Params) {
     fmt.Fprint(w, "Welcome!\n")
 }

 func Hello(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
     fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
 }

 func main() {
     router := apirouter.New(
			apirouter.GET("/", Index),
			apirouter.GET("/hello/:name", Hello),
		)

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

The registered pattern, against which the router matches incoming requests, can contain three types of parameters(default style):

	Syntax                      Type
 :name                       named parameter
 :name=regular-expressions   regular expression parameter
 *name                       wildcard parameter

Matching priority, on the example below the router will test the routes in the following order, /users/list then /users/:id=^\d+$ then /users/:id then /users/*page.

r:= apirouter.New(
	apirouter.GET("/users/:id",...),
	apirouter.GET(`/users/:id=^\d+$`,...),
	apirouter.GET("/users/*page",...),
	apirouter.GET("/users/list",...),
)

Named parameters are dynamic path segments. They match anything until the next '/' or the path end:

Pattern: /blog/:category/:post

Requests:
 /blog/go/request-routers            match: category="go", post="request-routers"
 /blog/go/request-routers/           no match
 /blog/go/                           no match
 /blog/go/request-routers/comments   no match

If a parameter must match an exact pattern (digits only, for example), you can also set a regular expression constraint just after the parameter name and `=`.

Pattern: /users/:id=^\d+$

Requests:
 /users/123            match: id="123"
 /users/admin          no match
 /users/123/           no match

Wildcard parameters match anything until the path end, not including the directory index (the '/' before the '*'). Since they match anything until the end, wildcard parameters must always be the final path element.

Path: /files/*filepath

Requests:
 /files/                             match: filepath=""
 /files/LICENSE                      match: filepath="LICENSE"
 /files/templates/article.html       match: filepath="templates/article.html"
 /files                              no match

The value of parameters is saved as a Params. The Params is passed to the Handler func as a third parameter. If the handler is registered with Handle or HandleFunc, params is stored in request's context.

Example:

r:=apirouter.New(
	apirouter.GET("/streams/:id", func(w http.ResponseWriter, r *http.Request, ps apirouter.Params) {
		id := ps.ByName("id")
		fmt.Fprintf(w, "Page of stream #%s", id)
	}),

	apirouter.HandleFunc("GET","/users/:id", func(w http.ResponseWriter, r *http.Request) {
		ps := apirouter.PathParams(r.Context())
		id := ps.ByName("id")
		fmt.Fprintf(w, "Page of user #%s", id)
	}),
)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func WrapHandler

func WrapHandler(h http.Handler, interceptors ...Interceptor) http.Handler

WrapHandler wraps the http.Handler with the interceptors and transforms it into a different http.Handler

func WrapHandlerFunc

func WrapHandlerFunc(h http.HandlerFunc, interceptors ...Interceptor) http.HandlerFunc

WrapHandlerFunc wraps the http.HandlerFunc with the interceptors and transforms it into a different http.HandlerFunc

Types

type Handler

type Handler func(w http.ResponseWriter, r *http.Request, ps Params)

Handler is a function that can be registered to a router to handle HTTP requests.

Compared with http.HandlerFunc, it add the third parameter for the path parameters extracted from the HTTP request.

func Wrap

func Wrap(h Handler, interceptors ...Interceptor) Handler

Wrap wraps the hanndler with the interceptors and transforms it into a different handler

type Interceptor

type Interceptor interface {
	// PreHandle achieve the preprocessing of the HTTP request handler (such as check login).
	// return value:
	// true - continue the process (such as calling the next interceptor or Handler);
	// false - interrupte the process(such as the logon check fails)
	// and will not continue to invoke other interceptors or Handler,
	// in which case we need to generate a response through w.
	PreHandle(w http.ResponseWriter, r *http.Request) bool

	// PostHandle achieve the post-processing of the HTTP request handler.
	PostHandle(r *http.Request)
}

Interceptor provides a hook to intercept the execution of the HTTP request handler.

func ChainInterceptor

func ChainInterceptor(its ...Interceptor) Interceptor

ChainInterceptor creates a single interceptor out of a chain of many interceptors.

PreHandle's Execution is done in left-to-right order. For example ChainInterceptor(one, two, three) will execute one before two before three, and three will see r changes of one and two. PostHandle's Execution is done in right-to-left order.

func NewInterceptor

func NewInterceptor(pre PreInterceptor, post PostInterceptor) Interceptor

NewInterceptor returns a new Interceptor.

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option represents all possible options to the New() function

func API

func API(method string, pattern string, handler Handler) Option

API creates the option to registers api.

  • method: supported HTTP methods,
  • pattern: url path matched pattern,
  • handler: http request handler.

func DELETE

func DELETE(pattern string, handler Handler) Option

DELETE is a shortcut for API(http.MethodDelete, pattern, handler)

func GET

func GET(pattern string, handler Handler) Option

GET is a shortcut for API(http.MethodGet, pattern, handler)

func HEAD(pattern string, handler Handler) Option

HEAD is a shortcut for API(http.MethodHead, pattern, handler)

func Handle

func Handle(method string, pattern string, handler http.Handler) Option

Handle creates the option to perform similar actions with the standard library http.Handle.

func HandleFunc

func HandleFunc(method string, pattern string, handler func(http.ResponseWriter, *http.Request)) Option

HandleFunc creates the option to perform similar actions with the standard library http.HandleFunc.

func NotFoundHandler

func NotFoundHandler(handler http.Handler) Option

NotFoundHandler creates the option to set a request handler that replies to each request with a “404 page not found” reply.

func OPTIONS

func OPTIONS(pattern string, handler Handler) Option

OPTIONS is a shortcut for API(http.MethodOptions, pattern, handler)

func PATCH

func PATCH(pattern string, handler Handler) Option

PATCH is a shortcut for API(http.MethodPatch, pattern, handler)

func POST

func POST(pattern string, handler Handler) Option

POST is a shortcut for API(http.MethodPost, pattern, handler)

func PUT

func PUT(pattern string, handler Handler) Option

PUT is a shortcut for API(http.MethodPut, pattern, handler)

type Params

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

Params holds the path parameters extracted from the HTTP request.

func PathParams

func PathParams(c context.Context) *Params

PathParams pulls the path parameters from a request context, or returns nil if none are present.

func (Params) ByName

func (p Params) ByName(name string) string

ByName returns the value of the first parameter that matched the given name. Otherwise, an empty string is returned.

func (Params) Count

func (p Params) Count() int

Count returns the number of parameters.

func (Params) Name

func (p Params) Name(i int) string

Name returns the parameter name of the given index.

func (Params) Value

func (p Params) Value(i int) string

Value returns the parameter value of ther given index.

type Pattern

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

Pattern a parsed representation of path pattern. eg github.com/googleapis/googleapis/google/api/http.proto.

func MustPattern

func MustPattern(p Pattern, err error) Pattern

MustPattern is a helper function which makes it easier to call NewPattern or NewGRPCPattern in variable initialization.

func NewGRPCPattern

func NewGRPCPattern(pattern string, regexps *[]*regexp.Regexp) (p Pattern, err error)

NewGRPCPattern creates a gRPC style's new Pattern from the given original pattern. "regexps" is a list of regular expressions shared between multiple patterns.

The syntax of the pattern string is as follows:

Pattern		= "/" Segments [ Verb ]
Segments	= Segment { "/" Segment }
Segment		= LITERAL | Parameter
Parameter	= Anonymous | Named
Anonymous	= "*" | "**"
Named		= "{" FieldPath [ "=" Wildcard ] "}"
Wildcard	= "*" | "**" | Regexp
FieldPath	= IDENT { "." IDENT }
Verb		= ":" LITERAL

func NewPattern

func NewPattern(pattern string, regexps *[]*regexp.Regexp) (p Pattern, err error)

NewPattern creates a default style's new Pattern from the given original pattern. "regexps" is a list of regular expressions shared between multiple patterns.

The syntax of the pattern string is as follows:

Pattern		= "/" Segments
Segments	= Segment { "/" Segment }
Segment		= LITERAL | Parameter
Parameter	= Anonymous | Named
Anonymous	= ":" | "*"
Named		= ":" FieldPath [ "=" Regexp ] | "*" FieldPath
FieldPath	= IDENT { "." IDENT }

func (Pattern) Field

func (p Pattern) Field(i int) string

Field returns a pattern's i'th field name.

func (Pattern) Key

func (p Pattern) Key() string

Key returns the key value of the pattern.

func (Pattern) NumField

func (p Pattern) NumField() int

NumField returns a pattern's field count.

func (Pattern) Pattern

func (p Pattern) Pattern() string

Pattern returns the original pattern (example: /v1/users/{id})

func (Pattern) Verb

func (p Pattern) Verb() string

Verb returns the VERB part of the path pattern. It is empty if the pattern does not have VERB part.

type PostInterceptor

type PostInterceptor func(r *http.Request)

PostInterceptor provides a hook function to intercept after the HTTP request handler is executed.

func (PostInterceptor) PostHandle

func (f PostInterceptor) PostHandle(r *http.Request)

PostHandle implements Intercetor.PostHandle.

func (PostInterceptor) PreHandle

func (f PostInterceptor) PreHandle(w http.ResponseWriter, r *http.Request) bool

PreHandle implements Intercetor.PreHandle with no-op. It always returns true.

type PreInterceptor

type PreInterceptor func(w http.ResponseWriter, r *http.Request) bool

PreInterceptor provides a hook function to intercept before the HTTP request handler is executed.

func (PreInterceptor) PostHandle

func (f PreInterceptor) PostHandle(r *http.Request)

PostHandle implements Intercetor.PostHandle with no-op.

func (PreInterceptor) PreHandle

func (f PreInterceptor) PreHandle(w http.ResponseWriter, r *http.Request) bool

PreHandle implements Intercetor.PreHandle.

type Router

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

Router implements http.Handler interface. It matches the URL of each incoming request, and calls the handler that most closely matches the URL.

NOTES: The zero value for Route is not available, it must be created with call New() function.

func New

func New(options ...Option) *Router

New returns a new Router,which is initialized with the given options and default pattern style.

The syntax of the pattern reference apirouter.NewPattern.

func NewForGRPC

func NewForGRPC(options ...Option) *Router

NewForGRPC returns a new Router,which is initialized with the given options and gRPC pattern style.

The syntax of the pattern reference apirouter.NewGRPCPattern.

func (*Router) Match

func (r *Router) Match(method string, path string) (h Handler, params Params)

Match returns the handler to use and path params matched the given method and path.

If there is no registered handler that applies to the given method and path, Match returns a nil handler and an empty path parameters.

func (*Router) ServeHTTP

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

ServeHTTP dispatches the request to the first handler whose matches to req.Method and req.Path.

Jump to

Keyboard shortcuts

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