Skip to content

Instantly share code, notes, and snippets.

@stevenferrer
Forked from Hunsin/router.go
Created November 19, 2021 14:58
Show Gist options
  • Save stevenferrer/2a22f555bcf54d923d26190deb42f259 to your computer and use it in GitHub Desktop.
Save stevenferrer/2a22f555bcf54d923d26190deb42f259 to your computer and use it in GitHub Desktop.

Revisions

  1. @Hunsin Hunsin revised this gist Sep 9, 2020. 2 changed files with 85 additions and 105 deletions.
    123 changes: 61 additions & 62 deletions router.go
    Original file line number Diff line number Diff line change
    @@ -1,106 +1,105 @@
    package router

    import (
    "context"
    "net/http"
    gpath "path"

    "github.com/julienschmidt/httprouter"
    )

    type middleware func(httprouter.Handle) httprouter.Handle
    // Param returns the named URL parameter from a request context.
    func Param(ctx context.Context, name string) string {
    if p := httprouter.ParamsFromContext(ctx); p != nil {
    return p.ByName(name)
    }
    return ""
    }

    // A Middleware chains http.Handlers.
    type Middleware func(http.Handler) http.Handler

    // Router is a http.Handler that wraps httprouter.Router with additional features.
    // A Router is a http.Handler which supports routing and middlewares.
    type Router struct {
    middlewares []middleware
    middlewares []Middleware
    path string
    router *httprouter.Router
    root *httprouter.Router
    }

    // New returns *Router with a new initialized *httprouter.Router embedded.
    // New creates a new Router.
    func New() *Router {
    return &Router{router: httprouter.New()}
    return &Router{root: httprouter.New(), path: "/"}
    }

    func (r *Router) joinPath(path string) string {
    if (r.path + path)[0] != '/' {
    panic("path should start with '/' in path '" + path + "'.")
    }

    return r.path + path
    }

    // Group returns new *Router with given path and middlewares.
    // It should be used for handles which have same path prefix or common middlewares.
    func (r *Router) Group(path string, m ...middleware) *Router {
    if path[len(path)-1] == '/' {
    path = path[:len(path)-1]
    }
    // Group returns a new Router with given path and middlewares.
    // It should be used for handlers which have same path prefix or
    // common middlewares.
    func (r *Router) Group(path string, m ...Middleware) *Router {
    return &Router{
    middlewares: append(m, r.middlewares...),
    path: r.joinPath(path),
    router: r.router,
    path: gpath.Join(r.path, path),
    root: r.root,
    }
    }

    // Use appends new middleware to current Router.
    func (r *Router) Use(m ...middleware) *Router {
    // Use appends new middlewares to current Router.
    func (r *Router) Use(m ...Middleware) *Router {
    r.middlewares = append(m, r.middlewares...)
    return r
    }

    // Handle registers a new request handle combined with middlewares.
    func (r *Router) Handle(method, path string, handle httprouter.Handle) {
    // Handle registers a new request handler combined with middlewares.
    func (r *Router) Handle(method, path string, handler http.Handler) {
    for _, v := range r.middlewares {
    handle = v(handle)
    handler = v(handler)
    }
    r.router.Handle(method, r.joinPath(path), handle)
    r.root.Handler(method, gpath.Join(r.path, path), handler)
    }

    // GET is a shortcut for Router.Handle("GET", path, handle)
    func (r *Router) GET(path string, handle httprouter.Handle) {
    r.Handle("GET", path, handle)
    // GET is a shortcut for r.Handle("GET", path, handler)
    func (r *Router) GET(path string, handler http.HandlerFunc) {
    r.Handle(http.MethodGet, path, handler)
    }

    // HEAD is a shortcut for Router.Handle("HEAD", path, handle)
    func (r *Router) HEAD(path string, handle httprouter.Handle) {
    r.Handle("HEAD", path, handle)
    // HEAD is a shortcut for r.Handle("HEAD", path, handler)
    func (r *Router) HEAD(path string, handler http.HandlerFunc) {
    r.Handle(http.MethodHead, path, handler)
    }

    // OPTIONS is a shortcut for Router.Handle("OPTIONS", path, handle)
    func (r *Router) OPTIONS(path string, handle httprouter.Handle) {
    r.Handle("OPTIONS", path, handle)
    // OPTIONS is a shortcut for r.Handle("OPTIONS", path, handler)
    func (r *Router) OPTIONS(path string, handler http.HandlerFunc) {
    r.Handle(http.MethodOptions, path, handler)
    }

    // POST is a shortcut for Router.Handle("POST", path, handle)
    func (r *Router) POST(path string, handle httprouter.Handle) {
    r.Handle("POST", path, handle)
    // POST is a shortcut for r.Handle("POST", path, handler)
    func (r *Router) POST(path string, handler http.HandlerFunc) {
    r.Handle(http.MethodPost, path, handler)
    }

    // PUT is a shortcut for Router.Handle("PUT", path, handle)
    func (r *Router) PUT(path string, handle httprouter.Handle) {
    r.Handle("PUT", path, handle)
    // PUT is a shortcut for r.Handle("PUT", path, handler)
    func (r *Router) PUT(path string, handler http.HandlerFunc) {
    r.Handle(http.MethodPut, path, handler)
    }

    // PATCH is a shortcut for Router.Handle("PATCH", path, handle)
    func (r *Router) PATCH(path string, handle httprouter.Handle) {
    r.Handle("PATCH", path, handle)
    // PATCH is a shortcut for r.Handle("PATCH", path, handler)
    func (r *Router) PATCH(path string, handler http.HandlerFunc) {
    r.Handle(http.MethodPatch, path, handler)
    }

    // DELETE is a shortcut for Router.Handle("DELETE", path, handle)
    func (r *Router) DELETE(path string, handle httprouter.Handle) {
    r.Handle("DELETE", path, handle)
    // DELETE is a shortcut for r.Handle("DELETE", path, handler)
    func (r *Router) DELETE(path string, handler http.HandlerFunc) {
    r.Handle(http.MethodDelete, path, handler)
    }

    // Handler is an adapter for http.Handler.
    func (r *Router) Handler(method, path string, handler http.Handler) {
    handle := func(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
    handler.ServeHTTP(w, req)
    }
    r.Handle(method, path, handle)
    // HandleFunc is an adapter for http.HandlerFunc.
    func (r *Router) HandleFunc(method, path string, handler http.HandlerFunc) {
    r.Handle(method, path, handler)
    }

    // HandlerFunc is an adapter for http.HandlerFunc.
    func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
    r.Handler(method, path, handler)
    // NotFound sets the handler which is called if the request path doesn't match
    // any routes. It overwrites the previous setting.
    func (r *Router) NotFound(handler http.Handler) {
    r.root.NotFound = handler
    }

    // Static serves files from given root directory.
    @@ -109,19 +108,19 @@ func (r *Router) Static(path, root string) {
    panic("path should end with '/*filepath' in path '" + path + "'.")
    }

    base := r.joinPath(path[:len(path)-9])
    base := gpath.Join(r.path, path[:len(path)-9])
    fileServer := http.StripPrefix(base, http.FileServer(http.Dir(root)))

    r.Handler("GET", path, fileServer)
    r.Handle(http.MethodGet, path, fileServer)
    }

    // File serves the named file.
    func (r *Router) File(path, name string) {
    r.HandlerFunc("GET", path, func(w http.ResponseWriter, req *http.Request) {
    r.HandleFunc(http.MethodGet, path, func(w http.ResponseWriter, req *http.Request) {
    http.ServeFile(w, req, name)
    })
    }

    func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    r.router.ServeHTTP(w, req)
    r.root.ServeHTTP(w, req)
    }
    67 changes: 24 additions & 43 deletions router_test.go
    Original file line number Diff line number Diff line change
    @@ -6,17 +6,15 @@ import (
    "net/http/httptest"
    "os"
    "testing"

    "github.com/julienschmidt/httprouter"
    )

    func TestHandle(t *testing.T) {
    router := New()

    h := func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    h := func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    }
    router.Handle("GET", "/", h)
    router.Handle("GET", "/", http.HandlerFunc(h))

    r := httptest.NewRequest("GET", "/", nil)
    w := httptest.NewRecorder()
    @@ -27,30 +25,13 @@ func TestHandle(t *testing.T) {
    }
    }

    func TestHandler(t *testing.T) {
    router := New()

    h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })
    router.Handler("GET", "/", h)

    r := httptest.NewRequest("GET", "/", nil)
    w := httptest.NewRecorder()

    router.ServeHTTP(w, r)
    if w.Code != http.StatusTeapot {
    t.Error("Test Handler failed")
    }
    }

    func TestHandlerFunc(t *testing.T) {
    func TestHandleFunc(t *testing.T) {
    router := New()

    h := func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    }
    router.HandlerFunc("GET", "/", h)
    router.HandleFunc("GET", "/", h)

    r := httptest.NewRequest("GET", "/", nil)
    w := httptest.NewRecorder()
    @@ -64,31 +45,31 @@ func TestHandlerFunc(t *testing.T) {
    func TestMethod(t *testing.T) {
    router := New()

    router.DELETE("/delete", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    router.DELETE("/delete", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    router.GET("/get", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    router.GET("/get", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    router.HEAD("/head", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    router.HEAD("/head", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    router.OPTIONS("/options", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    router.OPTIONS("/options", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    router.PATCH("/patch", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    router.PATCH("/patch", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    router.POST("/post", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    router.POST("/post", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    router.PUT("/put", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    router.PUT("/put", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    @@ -118,19 +99,19 @@ func TestGroup(t *testing.T) {
    bar := router.Group("/bar")
    baz := foo.Group("/baz")

    foo.HandlerFunc("GET", "", func(w http.ResponseWriter, _ *http.Request) {
    foo.HandleFunc("GET", "", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    foo.HandlerFunc("GET", "/group", func(w http.ResponseWriter, _ *http.Request) {
    foo.HandleFunc("GET", "/group", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    bar.HandlerFunc("GET", "/group", func(w http.ResponseWriter, _ *http.Request) {
    bar.HandleFunc("GET", "/group", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    baz.HandlerFunc("GET", "/group", func(w http.ResponseWriter, _ *http.Request) {
    baz.HandleFunc("GET", "/group", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    @@ -150,21 +131,21 @@ func TestGroup(t *testing.T) {
    func TestMiddleware(t *testing.T) {
    var use, group bool

    router := New().Use(func(next httprouter.Handle) httprouter.Handle {
    return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    router := New().Use(func(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    use = true
    next(w, r, ps)
    }
    next.ServeHTTP(w, r)
    })
    })

    foo := router.Group("/foo", func(next httprouter.Handle) httprouter.Handle {
    return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    foo := router.Group("/foo", func(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    group = true
    next(w, r, ps)
    }
    next.ServeHTTP(w, r)
    })
    })

    foo.HandlerFunc("GET", "/bar", func(w http.ResponseWriter, _ *http.Request) {
    foo.HandleFunc("GET", "/bar", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

  2. @Hunsin Hunsin revised this gist Jan 5, 2018. No changes.
  3. @Hunsin Hunsin revised this gist Jan 5, 2018. 2 changed files with 10 additions and 10 deletions.
    4 changes: 2 additions & 2 deletions router.go
    Original file line number Diff line number Diff line change
    @@ -15,8 +15,8 @@ type Router struct {
    router *httprouter.Router
    }

    // NewRouter returns *Router with a new initialized *httprouter.Router embedded.
    func NewRouter() *Router {
    // New returns *Router with a new initialized *httprouter.Router embedded.
    func New() *Router {
    return &Router{router: httprouter.New()}
    }

    16 changes: 8 additions & 8 deletions router_test.go
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@ import (
    )

    func TestHandle(t *testing.T) {
    router := NewRouter()
    router := New()

    h := func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    w.WriteHeader(http.StatusTeapot)
    @@ -28,7 +28,7 @@ func TestHandle(t *testing.T) {
    }

    func TestHandler(t *testing.T) {
    router := NewRouter()
    router := New()

    h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    @@ -45,7 +45,7 @@ func TestHandler(t *testing.T) {
    }

    func TestHandlerFunc(t *testing.T) {
    router := NewRouter()
    router := New()

    h := func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    @@ -62,7 +62,7 @@ func TestHandlerFunc(t *testing.T) {
    }

    func TestMethod(t *testing.T) {
    router := NewRouter()
    router := New()

    router.DELETE("/delete", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    w.WriteHeader(http.StatusTeapot)
    @@ -113,7 +113,7 @@ func TestMethod(t *testing.T) {
    }

    func TestGroup(t *testing.T) {
    router := NewRouter()
    router := New()
    foo := router.Group("/foo")
    bar := router.Group("/bar")
    baz := foo.Group("/baz")
    @@ -150,7 +150,7 @@ func TestGroup(t *testing.T) {
    func TestMiddleware(t *testing.T) {
    var use, group bool

    router := NewRouter().Use(func(next httprouter.Handle) httprouter.Handle {
    router := New().Use(func(next httprouter.Handle) httprouter.Handle {
    return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    use = true
    next(w, r, ps)
    @@ -194,7 +194,7 @@ func TestStatic(t *testing.T) {
    }

    pwd, _ := os.Getwd()
    router := NewRouter()
    router := New()
    router.Static("/*filepath", pwd)

    for i := range files {
    @@ -222,7 +222,7 @@ func TestFile(t *testing.T) {
    f.Sync()
    f.Close()

    router := NewRouter()
    router := New()
    router.File("/file", "temp_file")

    r := httptest.NewRequest("GET", "/file", nil)
  4. @Hunsin Hunsin revised this gist Dec 15, 2017. No changes.
  5. @Hunsin Hunsin revised this gist Dec 15, 2017. 1 changed file with 239 additions and 0 deletions.
    239 changes: 239 additions & 0 deletions router_test.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,239 @@
    package router

    import (
    "io/ioutil"
    "net/http"
    "net/http/httptest"
    "os"
    "testing"

    "github.com/julienschmidt/httprouter"
    )

    func TestHandle(t *testing.T) {
    router := NewRouter()

    h := func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    w.WriteHeader(http.StatusTeapot)
    }
    router.Handle("GET", "/", h)

    r := httptest.NewRequest("GET", "/", nil)
    w := httptest.NewRecorder()

    router.ServeHTTP(w, r)
    if w.Code != http.StatusTeapot {
    t.Error("Test Handle failed")
    }
    }

    func TestHandler(t *testing.T) {
    router := NewRouter()

    h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })
    router.Handler("GET", "/", h)

    r := httptest.NewRequest("GET", "/", nil)
    w := httptest.NewRecorder()

    router.ServeHTTP(w, r)
    if w.Code != http.StatusTeapot {
    t.Error("Test Handler failed")
    }
    }

    func TestHandlerFunc(t *testing.T) {
    router := NewRouter()

    h := func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    }
    router.HandlerFunc("GET", "/", h)

    r := httptest.NewRequest("GET", "/", nil)
    w := httptest.NewRecorder()

    router.ServeHTTP(w, r)
    if w.Code != http.StatusTeapot {
    t.Error("Test HandlerFunc failed")
    }
    }

    func TestMethod(t *testing.T) {
    router := NewRouter()

    router.DELETE("/delete", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    w.WriteHeader(http.StatusTeapot)
    })

    router.GET("/get", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    w.WriteHeader(http.StatusTeapot)
    })

    router.HEAD("/head", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    w.WriteHeader(http.StatusTeapot)
    })

    router.OPTIONS("/options", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    w.WriteHeader(http.StatusTeapot)
    })

    router.PATCH("/patch", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    w.WriteHeader(http.StatusTeapot)
    })

    router.POST("/post", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    w.WriteHeader(http.StatusTeapot)
    })

    router.PUT("/put", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
    w.WriteHeader(http.StatusTeapot)
    })

    samples := map[string]string{
    "DELETE": "/delete",
    "GET": "/get",
    "HEAD": "/head",
    "OPTIONS": "/options",
    "PATCH": "/patch",
    "POST": "/post",
    "PUT": "/put",
    }
    for method, path := range samples {
    r := httptest.NewRequest(method, path, nil)
    w := httptest.NewRecorder()

    router.ServeHTTP(w, r)
    if w.Code != http.StatusTeapot {
    t.Errorf("Path %s not registered", path)
    }
    }
    }

    func TestGroup(t *testing.T) {
    router := NewRouter()
    foo := router.Group("/foo")
    bar := router.Group("/bar")
    baz := foo.Group("/baz")

    foo.HandlerFunc("GET", "", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    foo.HandlerFunc("GET", "/group", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    bar.HandlerFunc("GET", "/group", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    baz.HandlerFunc("GET", "/group", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    samples := []string{"/foo", "/foo/group", "/foo/baz/group", "/bar/group"}

    for _, path := range samples {
    r := httptest.NewRequest("GET", path, nil)
    w := httptest.NewRecorder()

    router.ServeHTTP(w, r)
    if w.Code != http.StatusTeapot {
    t.Errorf("Grouped path %s not registered", path)
    }
    }
    }

    func TestMiddleware(t *testing.T) {
    var use, group bool

    router := NewRouter().Use(func(next httprouter.Handle) httprouter.Handle {
    return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    use = true
    next(w, r, ps)
    }
    })

    foo := router.Group("/foo", func(next httprouter.Handle) httprouter.Handle {
    return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    group = true
    next(w, r, ps)
    }
    })

    foo.HandlerFunc("GET", "/bar", func(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusTeapot)
    })

    r := httptest.NewRequest("GET", "/foo/bar", nil)
    w := httptest.NewRecorder()
    router.ServeHTTP(w, r)

    if !use {
    t.Error("Middleware registered by Use() under \"/\" not touched")
    }
    if !group {
    t.Error("Middleware registered by Group() under \"/foo\" not touched")
    }
    }

    func TestStatic(t *testing.T) {
    files := []string{"temp_1", "temp_2"}
    strs := []string{"test content", "static contents"}

    for i := range files {
    f, _ := os.Create(files[i])
    defer os.Remove(files[i])

    f.WriteString(strs[i])
    f.Sync()
    f.Close()
    }

    pwd, _ := os.Getwd()
    router := NewRouter()
    router.Static("/*filepath", pwd)

    for i := range files {
    r := httptest.NewRequest("GET", "/"+files[i], nil)
    w := httptest.NewRecorder()
    router.ServeHTTP(w, r)

    body := w.Result().Body
    defer body.Close()

    file, _ := ioutil.ReadAll(body)
    if string(file) != strs[i] {
    t.Error("Test Static failed")
    }
    }
    }

    func TestFile(t *testing.T) {
    str := "test_content"

    f, _ := os.Create("temp_file")
    defer os.Remove("temp_file")

    f.WriteString(str)
    f.Sync()
    f.Close()

    router := NewRouter()
    router.File("/file", "temp_file")

    r := httptest.NewRequest("GET", "/file", nil)
    w := httptest.NewRecorder()
    router.ServeHTTP(w, r)

    body := w.Result().Body
    defer body.Close()

    file, _ := ioutil.ReadAll(body)
    if string(file) != str {
    t.Error("Test File failed")
    }
    }
  6. @Hunsin Hunsin revised this gist Dec 15, 2017. 1 changed file with 33 additions and 32 deletions.
    65 changes: 33 additions & 32 deletions router.go
    Original file line number Diff line number Diff line change
    @@ -8,102 +8,103 @@ import (

    type middleware func(httprouter.Handle) httprouter.Handle

    // IRouter is a http.Handler that wraps httprouter.Router with additional features.
    type IRouter struct {
    // Router is a http.Handler that wraps httprouter.Router with additional features.
    type Router struct {
    middlewares []middleware
    path string
    router *httprouter.Router
    }

    // NewRouter returns *IRouter with a new initialized httprouter.Router attached.
    func NewRouter() *IRouter {
    return &IRouter{router: httprouter.New()}
    // NewRouter returns *Router with a new initialized *httprouter.Router embedded.
    func NewRouter() *Router {
    return &Router{router: httprouter.New()}
    }

    func (r *IRouter) joinPath(path string) string {
    func (r *Router) joinPath(path string) string {
    if (r.path + path)[0] != '/' {
    panic("path should start with '/' in path '" + path + "'.")
    }

    return r.path + path
    }

    // Group returns new *IRouter with given path and middlewares.
    // Group returns new *Router with given path and middlewares.
    // It should be used for handles which have same path prefix or common middlewares.
    func (r *IRouter) Group(path string, m ...middleware) *IRouter {
    func (r *Router) Group(path string, m ...middleware) *Router {
    if path[len(path)-1] == '/' {
    path = path[:len(path)-1]
    }
    return &IRouter{
    return &Router{
    middlewares: append(m, r.middlewares...),
    path: r.joinPath(path),
    router: r.router,
    }
    }

    // Use appends new middleware to current IRouter.
    func (r *IRouter) Use(m ...middleware) {
    // Use appends new middleware to current Router.
    func (r *Router) Use(m ...middleware) *Router {
    r.middlewares = append(m, r.middlewares...)
    return r
    }

    // Handle registers a new request handle combined with middlewares.
    func (r *IRouter) Handle(method, path string, handle httprouter.Handle) {
    func (r *Router) Handle(method, path string, handle httprouter.Handle) {
    for _, v := range r.middlewares {
    handle = v(handle)
    }
    r.router.Handle(method, r.joinPath(path), handle)
    }

    // GET is a shortcut for IRouter.Handle("GET", path, handle)
    func (r *IRouter) GET(path string, handle httprouter.Handle) {
    // GET is a shortcut for Router.Handle("GET", path, handle)
    func (r *Router) GET(path string, handle httprouter.Handle) {
    r.Handle("GET", path, handle)
    }

    // HEAD is a shortcut for IRouter.Handle("HEAD", path, handle)
    func (r *IRouter) HEAD(path string, handle httprouter.Handle) {
    // HEAD is a shortcut for Router.Handle("HEAD", path, handle)
    func (r *Router) HEAD(path string, handle httprouter.Handle) {
    r.Handle("HEAD", path, handle)
    }

    // OPTIONS is a shortcut for IRouter.Handle("OPTIONS", path, handle)
    func (r *IRouter) OPTIONS(path string, handle httprouter.Handle) {
    // OPTIONS is a shortcut for Router.Handle("OPTIONS", path, handle)
    func (r *Router) OPTIONS(path string, handle httprouter.Handle) {
    r.Handle("OPTIONS", path, handle)
    }

    // POST is a shortcut for IRouter.Handle("POST", path, handle)
    func (r *IRouter) POST(path string, handle httprouter.Handle) {
    // POST is a shortcut for Router.Handle("POST", path, handle)
    func (r *Router) POST(path string, handle httprouter.Handle) {
    r.Handle("POST", path, handle)
    }

    // PUT is a shortcut for IRouter.Handle("PUT", path, handle)
    func (r *IRouter) PUT(path string, handle httprouter.Handle) {
    // PUT is a shortcut for Router.Handle("PUT", path, handle)
    func (r *Router) PUT(path string, handle httprouter.Handle) {
    r.Handle("PUT", path, handle)
    }

    // PATCH is a shortcut for IRouter.Handle("PATCH", path, handle)
    func (r *IRouter) PATCH(path string, handle httprouter.Handle) {
    // PATCH is a shortcut for Router.Handle("PATCH", path, handle)
    func (r *Router) PATCH(path string, handle httprouter.Handle) {
    r.Handle("PATCH", path, handle)
    }

    // DELETE is a shortcut for IRouter.Handle("DELETE", path, handle)
    func (r *IRouter) DELETE(path string, handle httprouter.Handle) {
    // DELETE is a shortcut for Router.Handle("DELETE", path, handle)
    func (r *Router) DELETE(path string, handle httprouter.Handle) {
    r.Handle("DELETE", path, handle)
    }

    // Handler is an adapter for http.Handler.
    func (r *IRouter) Handler(method, path string, handler http.Handler) {
    func (r *Router) Handler(method, path string, handler http.Handler) {
    handle := func(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
    handler.ServeHTTP(w, req)
    }
    r.Handle(method, path, handle)
    }

    // HandlerFunc is an adapter for http.HandlerFunc.
    func (r *IRouter) HandlerFunc(method, path string, handler http.HandlerFunc) {
    func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
    r.Handler(method, path, handler)
    }

    // Static serves files from given root directory.
    func (r *IRouter) Static(path, root string) {
    func (r *Router) Static(path, root string) {
    if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
    panic("path should end with '/*filepath' in path '" + path + "'.")
    }
    @@ -115,12 +116,12 @@ func (r *IRouter) Static(path, root string) {
    }

    // File serves the named file.
    func (r *IRouter) File(path, name string) {
    func (r *Router) File(path, name string) {
    r.HandlerFunc("GET", path, func(w http.ResponseWriter, req *http.Request) {
    http.ServeFile(w, req, name)
    })
    }

    func (r *IRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    r.router.ServeHTTP(w, req)
    }
    }
  7. @Hunsin Hunsin revised this gist Jul 5, 2017. No changes.
  8. @Hunsin Hunsin created this gist Jul 4, 2017.
    126 changes: 126 additions & 0 deletions router.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,126 @@
    package router

    import (
    "net/http"

    "github.com/julienschmidt/httprouter"
    )

    type middleware func(httprouter.Handle) httprouter.Handle

    // IRouter is a http.Handler that wraps httprouter.Router with additional features.
    type IRouter struct {
    middlewares []middleware
    path string
    router *httprouter.Router
    }

    // NewRouter returns *IRouter with a new initialized httprouter.Router attached.
    func NewRouter() *IRouter {
    return &IRouter{router: httprouter.New()}
    }

    func (r *IRouter) joinPath(path string) string {
    if (r.path + path)[0] != '/' {
    panic("path should start with '/' in path '" + path + "'.")
    }

    return r.path + path
    }

    // Group returns new *IRouter with given path and middlewares.
    // It should be used for handles which have same path prefix or common middlewares.
    func (r *IRouter) Group(path string, m ...middleware) *IRouter {
    if path[len(path)-1] == '/' {
    path = path[:len(path)-1]
    }
    return &IRouter{
    middlewares: append(m, r.middlewares...),
    path: r.joinPath(path),
    router: r.router,
    }
    }

    // Use appends new middleware to current IRouter.
    func (r *IRouter) Use(m ...middleware) {
    r.middlewares = append(m, r.middlewares...)
    }

    // Handle registers a new request handle combined with middlewares.
    func (r *IRouter) Handle(method, path string, handle httprouter.Handle) {
    for _, v := range r.middlewares {
    handle = v(handle)
    }
    r.router.Handle(method, r.joinPath(path), handle)
    }

    // GET is a shortcut for IRouter.Handle("GET", path, handle)
    func (r *IRouter) GET(path string, handle httprouter.Handle) {
    r.Handle("GET", path, handle)
    }

    // HEAD is a shortcut for IRouter.Handle("HEAD", path, handle)
    func (r *IRouter) HEAD(path string, handle httprouter.Handle) {
    r.Handle("HEAD", path, handle)
    }

    // OPTIONS is a shortcut for IRouter.Handle("OPTIONS", path, handle)
    func (r *IRouter) OPTIONS(path string, handle httprouter.Handle) {
    r.Handle("OPTIONS", path, handle)
    }

    // POST is a shortcut for IRouter.Handle("POST", path, handle)
    func (r *IRouter) POST(path string, handle httprouter.Handle) {
    r.Handle("POST", path, handle)
    }

    // PUT is a shortcut for IRouter.Handle("PUT", path, handle)
    func (r *IRouter) PUT(path string, handle httprouter.Handle) {
    r.Handle("PUT", path, handle)
    }

    // PATCH is a shortcut for IRouter.Handle("PATCH", path, handle)
    func (r *IRouter) PATCH(path string, handle httprouter.Handle) {
    r.Handle("PATCH", path, handle)
    }

    // DELETE is a shortcut for IRouter.Handle("DELETE", path, handle)
    func (r *IRouter) DELETE(path string, handle httprouter.Handle) {
    r.Handle("DELETE", path, handle)
    }

    // Handler is an adapter for http.Handler.
    func (r *IRouter) Handler(method, path string, handler http.Handler) {
    handle := func(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
    handler.ServeHTTP(w, req)
    }
    r.Handle(method, path, handle)
    }

    // HandlerFunc is an adapter for http.HandlerFunc.
    func (r *IRouter) HandlerFunc(method, path string, handler http.HandlerFunc) {
    r.Handler(method, path, handler)
    }

    // Static serves files from given root directory.
    func (r *IRouter) Static(path, root string) {
    if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
    panic("path should end with '/*filepath' in path '" + path + "'.")
    }

    base := r.joinPath(path[:len(path)-9])
    fileServer := http.StripPrefix(base, http.FileServer(http.Dir(root)))

    r.Handler("GET", path, fileServer)
    }

    // File serves the named file.
    func (r *IRouter) File(path, name string) {
    r.HandlerFunc("GET", path, func(w http.ResponseWriter, req *http.Request) {
    http.ServeFile(w, req, name)
    })
    }

    func (r *IRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    r.router.ServeHTTP(w, req)
    }