Go Router + Middleware. Your Contexts.

Related tags

Routers web
Overview

gocraft/web GoDoc

gocraft/web is a Go mux and middleware package. We deal with casting and reflection so YOUR code can be statically typed. And we're fast.

Getting Started

From your GOPATH:

go get github.com/gocraft/web

Add a file server.go - for instance, src/myapp/server.go

package main

import (
	"github.com/gocraft/web"
	"fmt"
	"net/http"
	"strings"
)

type Context struct {
	HelloCount int
}

func (c *Context) SetHelloCount(rw web.ResponseWriter, req *web.Request, next web.NextMiddlewareFunc) {
	c.HelloCount = 3
	next(rw, req)
}

func (c *Context) SayHello(rw web.ResponseWriter, req *web.Request) {
	fmt.Fprint(rw, strings.Repeat("Hello ", c.HelloCount), "World!")
}

func main() {
	router := web.New(Context{}).                   // Create your router
		Middleware(web.LoggerMiddleware).           // Use some included middleware
		Middleware(web.ShowErrorsMiddleware).       // ...
		Middleware((*Context).SetHelloCount).       // Your own middleware!
		Get("/", (*Context).SayHello)               // Add a route
	http.ListenAndServe("localhost:3000", router)   // Start the server!
}

Run the server. It will be available on localhost:3000:

go run src/myapp/server.go

Features

  • Super fast and scalable. Added latency is from 3-9μs per request. Routing performance is O(log(N)) in the number of routes.
  • Your own contexts. Easily pass information between your middleware and handler with strong static typing.
  • Easy and powerful routing. Capture path variables. Validate path segments with regexps. Lovely API.
  • Middleware. Middleware can express almost any web-layer feature. We make it easy.
  • Nested routers, contexts, and middleware. Your app has an API, and admin area, and a logged out view. Each view needs different contexts and different middleware. We let you express this hierarchy naturally.
  • Embrace Go's net/http package. Start your server with http.ListenAndServe(), and work directly with http.ResponseWriter and http.Request.
  • Minimal. The core of gocraft/web is lightweight and minimal. Add optional functionality with our built-in middleware, or write your own middleware.

Performance

Performance is a first class concern. Every update to this package has its performance measured and tracked in BENCHMARK_RESULTS.

For minimal 'hello world' style apps, added latency is about 3μs. This grows to about 10μs for more complex apps (6 middleware functions, 3 levels of contexts, 150+ routes).

One key design choice we've made is our choice of routing algorithm. Most competing libraries use simple O(N) iteration over all routes to find a match. This is fine if you have only a handful of routes, but starts to break down as your app gets bigger. We use a tree-based router which grows in complexity at O(log(N)).

Application Structure

Making your router

The first thing you need to do is make a new router. Routers serve requests and execute middleware.

router := web.New(YourContext{})

Your context

Wait, what is YourContext{} and why do you need it? It can be any struct you want it to be. Here's an example of one:

type YourContext struct {
  User *User // Assumes you've defined a User type as well
}

Your context can be empty or it can have various fields in it. The fields can be whatever you want - it's your type! When a new request comes into the router, we'll allocate an instance of this struct and pass it to your middleware and handlers. This allows, for instance, a SetUser middleware to set a User field that can be read in the handlers.

Routes and handlers

Once you have your router, you can add routes to it. Standard HTTP verbs are supported.

router := web.New(YourContext{})
router.Get("/users", (*YourContext).UsersList)
router.Post("/users", (*YourContext).UsersCreate)
router.Put("/users/:id", (*YourContext).UsersUpdate)
router.Delete("/users/:id", (*YourContext).UsersDelete)
router.Patch("/users/:id", (*YourContext).UsersUpdate)
router.Get("/", (*YourContext).Root)

What is that funny (*YourContext).Root notation? It's called a method expression. It lets your handlers look like this:

func (c *YourContext) Root(rw web.ResponseWriter, req *web.Request) {
	if c.User != nil {
		fmt.Fprint(rw, "Hello,", c.User.Name)
	} else {
		fmt.Fprint(rw, "Hello, anonymous person")
	}
}

All method expressions do is return a function that accepts the type as the first argument. So your handler can also look like this:

func Root(c *YourContext, rw web.ResponseWriter, req *web.Request) {}

Of course, if you don't need a context for a particular action, you can also do that:

func Root(rw web.ResponseWriter, req *web.Request) {}

Note that handlers always need to accept two input parameters: web.ResponseWriter, and *web.Request, both of which wrap the standard http.ResponseWriter and *http.Request, respectively.

Middleware

You can add middleware to a router:

router := web.New(YourContext{})
router.Middleware((*YourContext).UserRequired)
// add routes, more middleware

This is what a middleware handler looks like:

func (c *YourContext) UserRequired(rw web.ResponseWriter, r *web.Request, next web.NextMiddlewareFunc) {
	user := userFromSession(r)  // Pretend like this is defined. It reads a session cookie and returns a *User or nil.
	if user != nil {
		c.User = user
		next(rw, r)
	} else {
		rw.Header().Set("Location", "/")
		rw.WriteHeader(http.StatusMovedPermanently)
		// do NOT call next()
	}
}

Some things to note about the above example:

  • We set fields in the context for future middleware / handlers to use.
  • We can call next(), or not. Not calling next() effectively stops the middleware stack.

Of course, generic middleware without contexts is supported:

func GenericMiddleware(rw web.ResponseWriter, r *web.Request, next web.NextMiddlewareFunc) {
	// ...
}

Nested routers

Nested routers let you run different middleware and use different contexts for different parts of your app. Some common scenarios:

  • You want to run an AdminRequired middleware on all your admin routes, but not on API routes. Your context needs a CurrentAdmin field.
  • You want to run an OAuth middleware on your API routes. Your context needs an AccessToken field.
  • You want to run session handling middleware on ALL your routes. Your context needs a Session field.

Let's implement that. Your contexts would look like this:

type Context struct {
	Session map[string]string
}

type AdminContext struct {
	*Context
	CurrentAdmin *User
}

type ApiContext struct {
	*Context
	AccessToken string
}

Note that we embed a pointer to the parent context in each subcontext. This is required.

Now that we have our contexts, let's create our routers:

rootRouter := web.New(Context{})
rootRouter.Middleware((*Context).LoadSession)

apiRouter := rootRouter.Subrouter(ApiContext{}, "/api")
apiRouter.Middleware((*ApiContext).OAuth)
apiRouter.Get("/tickets", (*ApiContext).TicketsIndex)

adminRouter := rootRouter.Subrouter(AdminContext{}, "/admin")
adminRouter.Middleware((*AdminContext).AdminRequired)

// Given the path namesapce for this router is "/admin", the full path of this route is "/admin/reports"
adminRouter.Get("/reports", (*AdminContext).Reports)

Note that each time we make a subrouter, we need to supply the context as well as a path namespace. The context CAN be the same as the parent context, and the namespace CAN just be "/" for no namespace.

Request lifecycle

The following is a detailed account of the request lifecycle:

  1. A request comes in. Yay! (follow along in router_serve.go if you'd like)
  2. Wrap the default Go http.ResponseWriter and http.Request in a web.ResponseWriter and web.Request, respectively (via structure embedding).
  3. Allocate a new root context. This context is passed into your root middleware.
  4. Execute middleware on the root router. We do this before we find a route!
  5. After all of the root router's middleware is executed, we'll run a 'virtual' routing middleware that determines the target route.
    • If the there's no route found, we'll execute the NotFound handler if supplied. Otherwise, we'll write a 404 response and start unwinding the root middlware.
  6. Now that we have a target route, we can allocate the context tree of the target router.
  7. Start executing middleware on the nested middleware leading up to the final router/route.
  8. After all middleware is executed, we'll run another 'virtual' middleware that invokes the final handler corresponding to the target route.
  9. Unwind all middleware calls (if there's any code after next() in the middleware, obviously that's going to run at some point).

Capturing path params; regexp conditions

You can capture path variables like this:

router.Get("/suggestions/:suggestion_id/comments/:comment_id")

In your handler, you can access them like this:

func (c *YourContext) Root(rw web.ResponseWriter, req *web.Request) {
	fmt.Fprint(rw, "Suggestion ID:", req.PathParams["suggestion_id"])
	fmt.Fprint(rw, "Comment ID:", req.PathParams["comment_id"])
}

You can also validate the format of your path params with a regexp. For instance, to ensure the 'ids' start with a digit:

router.Get("/suggestions/:suggestion_id:\\d.*/comments/:comment_id:\\d.*")

You can match any route past a certain point like this:

router.Get("/suggestions/:suggestion_id/comments/:comment_id/:*")

The path params will contain a “” member with the rest of your path. It is illegal to add any more paths past the “” path param, as it’s meant to match every path afterwards, in all cases.

For Example: /suggestions/123/comments/321/foo/879/bar/834

Elicits path params: * “suggestion_id”: 123, * “comment_id”: 321, * “*”: “foo/879/bar/834”

One thing you CANNOT currently do is use regexps outside of a path segment. For instance, optional path segments are not supported - you would have to define multiple routes that both point to the same handler. This design decision was made to enable efficient routing.

Not Found handlers

If a route isn't found, by default we'll return a 404 status and render the text "Not Found".

You can supply a custom NotFound handler on your root router:

router.NotFound((*Context).NotFound)

Your handler can optionally accept a pointer to the root context. NotFound handlers look like this:

func (c *Context) NotFound(rw web.ResponseWriter, r *web.Request) {
	rw.WriteHeader(http.StatusNotFound) // You probably want to return 404. But you can also redirect or do whatever you want.
	fmt.Fprintf(rw, "My Not Found")     // Render you own HTML or something!
}

OPTIONS handlers

If an OPTIONS request is made and routes with other methods are found for the requested path, then by default we'll return an empty response with an appropriate Access-Control-Allow-Methods header.

You can supply a custom OPTIONS handler on your root router:

router.OptionsHandler((*Context).OptionsHandler)

Your handler can optionally accept a pointer to the root context. OPTIONS handlers look like this:

func (c *Context) OptionsHandler(rw web.ResponseWriter, r *web.Request, methods []string) {
	rw.Header().Add("Access-Control-Allow-Methods", strings.Join(methods, ", "))
	rw.Header().Add("Access-Control-Allow-Origin", "*")
}

Error handlers

By default, if there's a panic in middleware or a handler, we'll return a 500 status and render the text "Application Error".

If you use the included middleware web.ShowErrorsMiddleware, a panic will result in a pretty backtrace being rendered in HTML. This is great for development.

You can also supply a custom Error handler on any router (not just the root router):

router.Error((*Context).Error)

Your handler can optionally accept a pointer to its corresponding context. Error handlers look like this:

func (c *Context) Error(rw web.ResponseWriter, r *web.Request, err interface{}) {
	rw.WriteHeader(http.StatusInternalServerError)
	fmt.Fprint(w, "Error", err)
}

Included middleware

We ship with three basic pieces of middleware: a logger, an exception printer, and a static file server. To use them:

router := web.New(Context{})
router.Middleware(web.LoggerMiddleware).
	Middleware(web.ShowErrorsMiddleware)

// The static middleware serves files. Examples:
// "GET /" will serve an index file at pwd/public/index.html
// "GET /robots.txt" will serve the file at pwd/public/robots.txt
// "GET /images/foo.gif" will serve the file at pwd/public/images/foo.gif
currentRoot, _ := os.Getwd()
router.Middleware(web.StaticMiddleware(path.Join(currentRoot, "public"), web.StaticOption{IndexFile: "index.html"}))

NOTE: You might not want to use web.ShowErrorsMiddleware in production. You can easily do something like this:

router := web.New(Context{})
router.Middleware(web.LoggerMiddleware)
if MyEnvironment == "development" {
	router.Middleware(web.ShowErrorsMiddleware)
}
// ...

Starting your server

Since web.Router implements http.Handler (eg, ServeHTTP(ResponseWriter, *Request)), you can easily plug it in to the standard Go http machinery:

router := web.New(Context{})
// ... Add routes and such.
http.ListenAndServe("localhost:8080", router)

Rendering responses

So now you routed a request to a handler. You have a web.ResponseWriter (http.ResponseWriter) and web.Request (http.Request). Now what?

// You can print to the ResponseWriter!
fmt.Fprintf(rw, "<html>I'm a web page!</html>")

This is currently where the implementation of this library stops. I recommend you read the documentation of net/http.

Extra Middlware

This package is going to keep the built-in middlware simple and lean. Extra middleware can be found across the web:

If you'd like me to link to your middleware, let me know with a pull request to this README.

gocraft

gocraft offers a toolkit for building web apps. Currently these packages are available:

  • gocraft/web - Go Router + Middleware. Your Contexts.
  • gocraft/dbr - Additions to Go's database/sql for super fast performance and convenience.
  • gocraft/health - Instrument your web apps with logging and metrics.
  • gocraft/work - Process background jobs in Go.

These packages were developed by the engineering team at UserVoice and currently power much of its infrastructure and tech stack.

Thanks & Authors

I use code/got inspiration from these excellent libraries:

Authors:

Comments
  • Set default fields for context

    Set default fields for context

    Would be nice to have a way to set default fields of a context struct that doesn't get zeroed on each request. For example DB session is only initialised once and shouldn't need to be set to the context on each request (if not necessary to overwrite it with a different DB session)

    opened by pengux 11
  • StaticMiddleware(path) precedence

    StaticMiddleware(path) precedence

    Using this code:

      path, _ := os.Getwd()
      path += "/public/media"
      router := web.New(Context{}).
    
      Middleware(web.LoggerMiddleware).           // Use some included middleware
      Middleware(web.ShowErrorsMiddleware).
      Middleware(web.StaticMiddleware(path)).
      Get("/index.html", (*Context).Main).
      Get("/", (*Context).Main).
      NotFound((*Context).NotFound)
    

    One can not just open: localhost:3000 in the browser because one is then always matched against the StaticMiddleware. But I would like that localhost:3000 opens the Main page instead of having to type, say localhost:3000/index.html every time.

    Am I missing something here?

    opened by qknight 7
  • a little fault of middleware in the subrouter, or solution needed.

    a little fault of middleware in the subrouter, or solution needed.

    func init() { rootRouter := web.New(Context{}).Middleware((_Context).LoginRequired) adminRouter := rootRouter.Subrouter(AdminContext{}, "/admin") adminRouter.Middleware((_AdminContext).AdminRequired) // http.Handle("/", rootRouter) }

    I'm trying gocraft in google's app engine. For the code above I found it's a bit unexpected results for me because only LoginRequired middleware been called when I go url "..../admin". I expect the AdminRequired middleware been called too since I've subrouter it. Add this code: /* adminRouter.Get("/", (*AdminContext).Handler) */ should work just like sample code. But I expect AdminRequired been called in any of http methods even I don't have the matching handlers. Sounds reasonable?

    opened by lightingland 7
  • Trouble with web.ResponseWriter.Flush()

    Trouble with web.ResponseWriter.Flush()

    Using a trivially modified example that sleeps in the middle of an output:

    func (c *Context) SayHello(rw web.ResponseWriter, req *web.Request) {
        Fprint(rw, strings.Repeat("Hello ", c.HelloCount), "World!")
    
        rw.Flush()
        time.Sleep(2 * time.Second)
    
        Fprint(rw, strings.Repeat("Hello ", c.HelloCount), "World!")
    }
    

    Running curl 127.0.0.1:3000 won't print anything until the entire output is finished. Adding a newline to the printed strings will make this example behave as expected.

    Is this an issue with gocraft/web, or is the problem at another layer? Do you forsee any issues with leaving requests open like this?

    I'm assuming that each request is inside its own gofunc, and so could be used to trivially implement server-sent events, an alternative to websockets that uses "plain" HTTP.

    opened by kofalt 5
  • Allow path prefix of root router to be set

    Allow path prefix of root router to be set

    Currently, there is no way to set the path prefix of the root router. It should be allowed as the application may not listen at the root of a server. An example is a REST API that may want to prefix all routes with /api or /v1 etc.

    opened by pengux 5
  • Integrating Outerware (middleware post handler processing)

    Integrating Outerware (middleware post handler processing)

    First off, thanks for building a flexible, yet simple router & middleware system.

    I was wondering, though -- Do you have any thoughts on how I might go about implementing "Outerware" (often called output filters)? Essentially, middleware that will get executed after the route handler gets processed? You could imagine that for ever web request you serve, you want to have a standard way of recording stats for that request without having to put the code in each handler. For instance:

    type YourContext struct {
      startTime time.Time
    }
    
    func (c *YourContext) StartRequest(rw web.ResponseWriter, req *web.Request, next web.NextMiddlewareFunc) {
      c.startTime = time.Now()
      next(rw, req)
    }
    
    func (c *YourContext) EndRequest(rw web.ResponseWriter, req *web.Request, next web.NextOuterwareFunc) {
      fmt.Fprint(rw, "request_time=", time.Since(c.startTime), " request_url=", req.URL.Path)
      next(rw, req)
    }
    
    router := web.New(YourContext{})
    router.Middleware((*YourContext).StartRequest)
    
    router.Get("/users", (*YourContext).UsersList)
    router.Post("/users", (*YourContext).UsersCreate)
    router.Put("/users/:id", (*YourContext).UsersUpdate)
    router.Delete("/users/:id", (*YourContext).UsersDelete)
    router.Patch("/users/:id", (*YourContext).UsersUpdate)
    router.Get("/", (*YourContext).Root)
    
    router.Outerware((*YourContext).EndRequest)
    

    You could also imagine this process would be useful for having the handlers prep the context with the output data, and a common outerware takes that data and generates the JSON response. I think the alternative would be to call a common function at the end of every handler. That solution just is not as flexible in terms of attaching different outerware to different routers and subrouters (not demonstrated in the above example).

    Thanks!

    opened by gtrevg 4
  • Custom Handler Types?

    Custom Handler Types?

    In the process of playing around with gocraft/web I've attempted to bring across a custom handler type that I use to save me some repetition when needing to return errors.

    type appHandler func(w http.ResponseWriter, r *http.Request) *appError
    
    type appError struct {
        code int
        err error
    }
    
    // Ensures appHandler satisfies the http.Handler interface
    func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
         if e := fn(w, r); e != nil {
              // Handle error cases
         }
    }
    

    The problem I'm having is integrating this into gocraft/web - which provides it's own wrappers (web.ResponseWriter and *web.Request).

    type appHandler func(w web.ResponseWriter, r *web.Request) *appError
    
    // Ensures appHandler satisfies the http.Handler interface
    func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
         // We can't pass in w, r here as they aren't web.ResponseWriter or web.Request
         if e := fn(w, r); e != nil {
              // Handle error cases
         }
    }
    

    Is there a way to achieve this? Looking at the docs, the GenericHandler type has a signature of func(web.ResponseWriter, *web.Request) but I'm not sure on how to reconcile this with wanting a ServeHTTP method for my handler types.

    opened by elithrar 4
  • Clone context instead of creating a new one

    Clone context instead of creating a new one

    This pull request modifies context creation: Instead of creating a new context on each request, a shallow copy of the context passed to New or Subrouter is created. This makes it possible to pass data into requests from the outside.

    One use case for this is dependency injection. For example, the context could look like this:

    type Context struct {
        UserService UserService // the dependency I'd like to inject
    
        UserName  string      // request-specific data
        SessionId string
    }
    

    I would initialize it like this:

    router := web.New(Context { UserService: myUserService }).
        Middleware((*Context).VerifyUser)
    

    And since the context of every request would be a shallow clone of my original context struct, I could access the user service from the middleware handler:

    func (ctx *Context) VerifyUser(w web.ResponseWriter, r *web.Request) {
        if !ctx.UserService.IsValidUser(ctx.UserName, ctx.SessionId) {
            // error handling...
        }
    

    As far as I can tell, the only way to access services from inside middleware right now is to use a global variable. This is a bit messy and not very composable. So unless I'm missing something and there already is a nice solution to this problem, I'd appreciate it if you would merge this.

    opened by hannobraun 4
  • Method Contexts via route reflect does not support pointers in said Context

    Method Contexts via route reflect does not support pointers in said Context

    Here is a snippet of a continuation of the nested routers and use of pointers with Context example from the docs: https://gist.github.com/bundah/d29cfc5fed6a6cb8428c

    Removing the struct pointer for User and following same pattern in web.New yields less crash but still incorrect behaviour:

    adminRouter := rootRouter.Subrouter(AdminContext{CurrentAdmin: User{Name: "Bill"}}, "/admin") // and also change the struct
    

    And finally, if we change these bits to:

    type AdminContext struct {
        *Context
        CurrentAdmin User
    }
    
    actx := AdminContext{CurrentAdmin: User{Name: "Bill"}}
    adminRouter := rootRouter.Subrouter(actx, "/admin")
        adminRouter.Get("/reports", (actx).Reports) //(*AdminContext).Reports)
    
    

    and curl that one, the name prints... Except that all requests now share actx instance and changing anywere at runtime via middleware etc crosses the request boundary (and can poison other requests).

    Please advise...

    opened by bundah 3
  • static_middleware hardcoded to accept string instead of http.Dir

    static_middleware hardcoded to accept string instead of http.Dir

    The HTTP package in go has a Filesystem interface so that you don't have to back your server with a real fileserver.

    In static_middleware.go, the string param type means that you can't use anything other than a real OS file; a call-path that accepted any http.Filesystem would let me use (e.g.) github.com/jteeuwen/go-bindata

    opened by DanielHeath 3
  • Return response code 500, when rendering error.

    Return response code 500, when rendering error.

    Responses by the ShowErrorsMiddleware returned response code 200, which confused my JavaScript code. The attached commit sets a 500 response code, making sure the error can be correctly handled on the client-side.

    opened by hannobraun 3
  • url with trailing  slash  match route      Get(

    url with trailing slash match route Get("/test/:uid" should be 404

    package main
    
    import (
    	"fmt"
    	"github.com/gocraft/web"
    	"net/http"
    )
    
    type Context struct {
    	HelloCount int
    }
    
    func (c *Context) Test(rw web.ResponseWriter, req *web.Request) {
    	fmt.Println(c)
    
    	fmt.Fprint(rw, req.PathParams["uid"])
    }
    
    func main() {
    	router := web.New(Context{}).
    		Get("/test/:uid", (*Context).Test)
    	http.ListenAndServe("localhost:3000", router)
    }
    
    

    curl http://localhost:3000/test/web curl http://localhost:3000/test/web/ all return 200

    int standart net/http

    package main
    import (
        "fmt"
        "net/http"
    )
    func main() {
        http.HandleFunc("/test/web", func (w http.ResponseWriter, r *http.Request) {
            fmt.Fprintf(w, "Welcome to my website!")
        })
       http.ListenAndServe(":8080", nil)
    }
    

    curl http://localhost:3000/test/web return 200 curl http://localhost:3000/test/web/ return 404

    opened by tablecell 0
  • Can you make public accessor to pathPrefix of a Router?

    Can you make public accessor to pathPrefix of a Router?

    In some scenarios, it's handy to get the full path of an endpoint like this: curPath := "/path/to/resource" subRouter.Get(curPath, fn) fullPath := subRouter.PathPrefix() + curPath

    Without public accessor, I have to pass sub-routers prefixes everywhere.

    opened by krestianskov 0
  • Can you replace a route?

    Can you replace a route?

    Hi folks,

    I have a wrapper library around gocraft/web that is used for all of our micro services. I want all of these services to have a /_health endpoint. So I set up one by default when the object is instantiated.

    I wanted to also have the ability for a service to pass in their own handler for this endpoint, when that happens, I want to remove the default created automatically, and replace it with the new one.

    Is there a way in your code to remove a route and replace it with a new one? If not, any suggestions?

    Thanks,

    opened by cajund 0
  • Header change not honored after middleware set it

    Header change not honored after middleware set it

    Hi Folks,

    I've wrapped your package in a library for a set of web services that always return JSON data (except for one case, this one, when downloading a file). I set the Content-Type with a piece of middleware:

    rRouter := &Router{}
    rRouter.mDispatch = web.New(Context{})
    rRouter.mDispatch.Middleware(func(rw web.ResponseWriter, r *web.Request, next web.NextMiddlewareFunc) {
    	rw.Header().Set("Content-Type", "application/json; charset=utf-8")
    	next(rw, r)
    })
    

    In one of my handlers, I want to override this for a file download:

    rw.WriteHeader(201)
    bytes := getContent(filename)
    rw.Header().Set("Content-Type","application/pdf")
    rw.Header().Set("Content-Disposition", "Attachment; filename="filename")
    rw.Write(bytes)
    

    When the endpoint is called, the content type comes down as application/json and not application/pdf as expected. I also have verified that the content type is set properly in the rw.Header map:

    log.Printf("%+v", rw.Header())
        2018/06/13 21:08:03 map[Content-Type:[application/pdf]]
    

    If I remove the middleware, this works fine. Any suggestions or insight as to what may be happening? Am I working with a copy of the rw var?

    Thanks in advance.

    opened by cajund 0
Releases(v1.1)
  • v1.1(Sep 3, 2014)

    Went from ~70% to ~96% test coverage.

    Minor features:

    • Proper HEAD support
    • Support more of net/http's interfaces: Hijacker, CloseNotifier, Flusher
    • Pluggable loggers for the LoggerMiddlware and the PanicNotifier.

    NOTE: Some types had their exportedness changed to private, so this release does have some minor backwards incompatibility if you happened to be using those types (there was no reason to).

    Source code(tar.gz)
    Source code(zip)
  • v1.0(Aug 30, 2014)

Owner
null
xujiajun/gorouter is a simple and fast HTTP router for Go. It is easy to build RESTful APIs and your web framework.

gorouter xujiajun/gorouter is a simple and fast HTTP router for Go. It is easy to build RESTful APIs and your web framework. Motivation I wanted a sim

徐佳军 528 Sep 27, 2022
Simple Golang HTTP router

Bellt Simple Golang HTTP router Bellt Package implements a request router with the aim of managing controller actions based on fixed and parameterized

Guilherme Caruso 54 Jul 18, 2022
Bxog is a simple and fast HTTP router for Go (HTTP request multiplexer).

Bxog is a simple and fast HTTP router for Go (HTTP request multiplexer). Usage An example of using the multiplexer: package main import ( "io" "net

Eduard 103 Sep 7, 2022
A high performance fasthttp request router that scales well

FastHttpRouter FastHttpRouter is forked from httprouter which is a lightweight high performance HTTP request router (also called multiplexer or just m

招牌疯子 869 Sep 1, 2022
FastRouter is a fast, flexible HTTP router written in Go.

FastRouter FastRouter is a fast, flexible HTTP router written in Go. FastRouter contains some customizable options, such as TrailingSlashesPolicy, Pan

Razon Yang 21 Apr 19, 2022
Go Server/API micro framework, HTTP request router, multiplexer, mux

?? gorouter Go Server/API micro framework, HTTP request router, multiplexer, mux. ?? ABOUT Contributors: Rafał Lorenz Want to contribute ? Feel free t

Rafał Lorenz 133 Sep 11, 2022
A high performance HTTP request router that scales well

HttpRouter HttpRouter is a lightweight high performance HTTP request router (also called multiplexer or just mux for short) for Go. In contrast to the

Julien Schmidt 14.5k Sep 27, 2022
High-speed, flexible tree-based HTTP router for Go.

httptreemux High-speed, flexible, tree-based HTTP router for Go. This is inspired by Julien Schmidt's httprouter, in that it uses a patricia tree, but

Daniel Imfeld 558 Sep 19, 2022
:rotating_light: Is a lightweight, fast and extensible zero allocation HTTP router for Go used to create customizable frameworks.

LARS LARS is a fast radix-tree based, zero allocation, HTTP router for Go. view examples. If looking for a more pure Go solution, be sure to check out

Go Playgound 390 Sep 27, 2022
A powerful HTTP router and URL matcher for building Go web servers with 🦍

gorilla/mux https://www.gorillatoolkit.org/pkg/mux Package gorilla/mux implements a request router and dispatcher for matching incoming requests to th

Gorilla Web Toolkit 17.4k Sep 25, 2022
An extremely fast Go (golang) HTTP router that supports regular expression route matching. Comes with full support for building RESTful APIs.

ozzo-routing You may consider using go-rest-api to jumpstart your new RESTful applications with ozzo-routing. Description ozzo-routing is a Go package

Ozzo Framework 440 Sep 14, 2022
Pure is a fast radix-tree based HTTP router

package pure Pure is a fast radix-tree based HTTP router that sticks to the native implementations of Go's "net/http" package; in essence, keeping the

Go Playgound 128 Aug 8, 2022
Echo Inspired Stand Alone URL Router

Vestigo - A Standalone Golang URL Router Abstract Many fast Golang URL routers are often embedded inside frameworks. Vestigo is a stand alone url rout

null 269 Sep 27, 2022
Go HTTP router

violetear Go HTTP router http://violetear.org Design Goals Keep it simple and small, avoiding extra complexity at all cost. KISS Support for static an

Nicolas Embriz 104 Aug 13, 2022
lightweight, idiomatic and composable router for building Go HTTP services

chi is a lightweight, idiomatic and composable router for building Go HTTP services. It's especially good at helping you write large REST API services

go-chi 12.3k Sep 28, 2022
Router socks. One port socks for all the others.

Router socks The next step after compromising a machine is to enumerate the network behind. Many tools exist to expose a socks port on the attacker's

null 62 Sep 18, 2022
:tongue: CleverGo is a lightweight, feature rich and high performance HTTP router for Go.

CleverGo CleverGo is a lightweight, feature rich and trie based high performance HTTP request router. go get -u clevergo.tech/clevergo English 简体中文 Fe

CleverGo Web Framework 252 Sep 14, 2022
Fast and flexible HTTP router

treemux - fast and flexible HTTP router Basic example Debug logging CORS example Error handling Rate limiting using Redis Gzip compression OpenTelemet

Vladimir Mihailenco 482 Sep 26, 2022
Fast, simple, and lightweight HTTP router for Golang

Sariaf Fast, simple and lightweight HTTP router for golang Install go get -u github.com/majidsajadi/sariaf Features Lightweight compatible with net/ht

defectivepixel 33 Aug 19, 2022