A powerful HTTP router and URL matcher for building Go web servers with 🦍

Overview

gorilla/mux

GoDoc CircleCI Sourcegraph

Gorilla Logo

https://www.gorillatoolkit.org/pkg/mux

Package gorilla/mux implements a request router and dispatcher for matching incoming requests to their respective handler.

The name mux stands for "HTTP request multiplexer". Like the standard http.ServeMux, mux.Router matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are:

  • It implements the http.Handler interface so it is compatible with the standard http.ServeMux.
  • Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers.
  • URL hosts, paths and query values can have variables with an optional regular expression.
  • Registered URLs can be built, or "reversed", which helps maintaining references to resources.
  • Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching.


Install

With a correctly configured Go toolchain:

go get -u github.com/gorilla/mux

Examples

Let's start registering a couple of URL paths and handlers:

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)
    r.HandleFunc("/products", ProductsHandler)
    r.HandleFunc("/articles", ArticlesHandler)
    http.Handle("/", r)
}

Here we register three routes mapping URL paths to handlers. This is equivalent to how http.HandleFunc() works: if an incoming request URL matches one of the paths, the corresponding handler is called passing (http.ResponseWriter, *http.Request) as parameters.

Paths can have variables. They are defined using the format {name} or {name:pattern}. If a regular expression pattern is not defined, the matched variable will be anything until the next slash. For example:

r := mux.NewRouter()
r.HandleFunc("/products/{key}", ProductHandler)
r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)

The names are used to create a map of route variables which can be retrieved calling mux.Vars():

func ArticlesCategoryHandler(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    w.WriteHeader(http.StatusOK)
    fmt.Fprintf(w, "Category: %v\n", vars["category"])
}

And this is all you need to know about the basic usage. More advanced options are explained below.

Matching Routes

Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables:

r := mux.NewRouter()
// Only matches if domain is "www.example.com".
r.Host("www.example.com")
// Matches a dynamic subdomain.
r.Host("{subdomain:[a-z]+}.example.com")

There are several other matchers that can be added. To match path prefixes:

r.PathPrefix("/products/")

...or HTTP methods:

r.Methods("GET", "POST")

...or URL schemes:

r.Schemes("https")

...or header values:

r.Headers("X-Requested-With", "XMLHttpRequest")

...or query values:

r.Queries("key", "value")

...or to use a custom matcher function:

r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
    return r.ProtoMajor == 0
})

...and finally, it is possible to combine several matchers in a single route:

r.HandleFunc("/products", ProductsHandler).
  Host("www.example.com").
  Methods("GET").
  Schemes("http")

Routes are tested in the order they were added to the router. If two routes match, the first one wins:

r := mux.NewRouter()
r.HandleFunc("/specific", specificHandler)
r.PathPrefix("/").Handler(catchAllHandler)

Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting".

For example, let's say we have several URLs that should only match when the host is www.example.com. Create a route for that host and get a "subrouter" from it:

r := mux.NewRouter()
s := r.Host("www.example.com").Subrouter()

Then register routes in the subrouter:

s.HandleFunc("/products/", ProductsHandler)
s.HandleFunc("/products/{key}", ProductHandler)
s.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)

The three URL paths we registered above will only be tested if the domain is www.example.com, because the subrouter is tested first. This is not only convenient, but also optimizes request matching. You can create subrouters combining any attribute matchers accepted by a route.

Subrouters can be used to create domain or path "namespaces": you define subrouters in a central place and then parts of the app can register its paths relatively to a given subrouter.

There's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths:

r := mux.NewRouter()
s := r.PathPrefix("/products").Subrouter()
// "/products/"
s.HandleFunc("/", ProductsHandler)
// "/products/{key}/"
s.HandleFunc("/{key}/", ProductHandler)
// "/products/{key}/details"
s.HandleFunc("/{key}/details", ProductDetailsHandler)

Static Files

Note that the path provided to PathPrefix() represents a "wildcard": calling PathPrefix("/static/").Handler(...) means that the handler will be passed any request that matches "/static/*". This makes it easy to serve static files with mux:

func main() {
    var dir string

    flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
    flag.Parse()
    r := mux.NewRouter()

    // This will serve files under http://localhost:8000/static/<filename>
    r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))

    srv := &http.Server{
        Handler:      r,
        Addr:         "127.0.0.1:8000",
        // Good practice: enforce timeouts for servers you create!
        WriteTimeout: 15 * time.Second,
        ReadTimeout:  15 * time.Second,
    }

    log.Fatal(srv.ListenAndServe())
}

Serving Single Page Applications

Most of the time it makes sense to serve your SPA on a separate web server from your API, but sometimes it's desirable to serve them both from one place. It's possible to write a simple handler for serving your SPA (for use with React Router's BrowserRouter for example), and leverage mux's powerful routing for your API endpoints.

package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/gorilla/mux"
)

// spaHandler implements the http.Handler interface, so we can use it
// to respond to HTTP requests. The path to the static directory and
// path to the index file within that static directory are used to
// serve the SPA in the given static directory.
type spaHandler struct {
	staticPath string
	indexPath  string
}

// ServeHTTP inspects the URL path to locate a file within the static dir
// on the SPA handler. If a file is found, it will be served. If not, the
// file located at the index path on the SPA handler will be served. This
// is suitable behavior for serving an SPA (single page application).
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // get the absolute path to prevent directory traversal
	path, err := filepath.Abs(r.URL.Path)
	if err != nil {
        // if we failed to get the absolute path respond with a 400 bad request
        // and stop
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

    // prepend the path with the path to the static directory
	path = filepath.Join(h.staticPath, path)

    // check whether a file exists at the given path
	_, err = os.Stat(path)
	if os.IsNotExist(err) {
		// file does not exist, serve index.html
		http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
		return
	} else if err != nil {
        // if we got an error (that wasn't that the file doesn't exist) stating the
        // file, return a 500 internal server error and stop
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

    // otherwise, use http.FileServer to serve the static dir
	http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r)
}

func main() {
	router := mux.NewRouter()

	router.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) {
		// an example API handler
		json.NewEncoder(w).Encode(map[string]bool{"ok": true})
	})

	spa := spaHandler{staticPath: "build", indexPath: "index.html"}
	router.PathPrefix("/").Handler(spa)

	srv := &http.Server{
		Handler: router,
		Addr:    "127.0.0.1:8000",
		// Good practice: enforce timeouts for servers you create!
		WriteTimeout: 15 * time.Second,
		ReadTimeout:  15 * time.Second,
	}

	log.Fatal(srv.ListenAndServe())
}

Registered URLs

Now let's see how to build registered URLs.

Routes can be named. All routes that define a name can have their URLs built, or "reversed". We define a name calling Name() on a route. For example:

r := mux.NewRouter()
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
  Name("article")

To build a URL, get the route and call the URL() method, passing a sequence of key/value pairs for the route variables. For the previous route, we would do:

url, err := r.Get("article").URL("category", "technology", "id", "42")

...and the result will be a url.URL with the following path:

"/articles/technology/42"

This also works for host and query value variables:

r := mux.NewRouter()
r.Host("{subdomain}.example.com").
  Path("/articles/{category}/{id:[0-9]+}").
  Queries("filter", "{filter}").
  HandlerFunc(ArticleHandler).
  Name("article")

// url.String() will be "http://news.example.com/articles/technology/42?filter=gorilla"
url, err := r.Get("article").URL("subdomain", "news",
                                 "category", "technology",
                                 "id", "42",
                                 "filter", "gorilla")

All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match.

Regex support also exists for matching Headers within a route. For example, we could do:

r.HeadersRegexp("Content-Type", "application/(text|json)")

...and the route will match both requests with a Content-Type of application/json as well as application/text

There's also a way to build only the URL host or path for a route: use the methods URLHost() or URLPath() instead. For the previous route, we would do:

// "http://news.example.com/"
host, err := r.Get("article").URLHost("subdomain", "news")

// "/articles/technology/42"
path, err := r.Get("article").URLPath("category", "technology", "id", "42")

And if you use subrouters, host and path defined separately can be built as well:

r := mux.NewRouter()
s := r.Host("{subdomain}.example.com").Subrouter()
s.Path("/articles/{category}/{id:[0-9]+}").
  HandlerFunc(ArticleHandler).
  Name("article")

// "http://news.example.com/articles/technology/42"
url, err := r.Get("article").URL("subdomain", "news",
                                 "category", "technology",
                                 "id", "42")

Walking Routes

The Walk function on mux.Router can be used to visit all of the routes that are registered on a router. For example, the following prints all of the registered routes:

package main

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/gorilla/mux"
)

func handler(w http.ResponseWriter, r *http.Request) {
	return
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/", handler)
	r.HandleFunc("/products", handler).Methods("POST")
	r.HandleFunc("/articles", handler).Methods("GET")
	r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT")
	r.HandleFunc("/authors", handler).Queries("surname", "{surname}")
	err := r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
		pathTemplate, err := route.GetPathTemplate()
		if err == nil {
			fmt.Println("ROUTE:", pathTemplate)
		}
		pathRegexp, err := route.GetPathRegexp()
		if err == nil {
			fmt.Println("Path regexp:", pathRegexp)
		}
		queriesTemplates, err := route.GetQueriesTemplates()
		if err == nil {
			fmt.Println("Queries templates:", strings.Join(queriesTemplates, ","))
		}
		queriesRegexps, err := route.GetQueriesRegexp()
		if err == nil {
			fmt.Println("Queries regexps:", strings.Join(queriesRegexps, ","))
		}
		methods, err := route.GetMethods()
		if err == nil {
			fmt.Println("Methods:", strings.Join(methods, ","))
		}
		fmt.Println()
		return nil
	})

	if err != nil {
		fmt.Println(err)
	}

	http.Handle("/", r)
}

Graceful Shutdown

Go 1.8 introduced the ability to gracefully shutdown a *http.Server. Here's how to do that alongside mux:

package main

import (
    "context"
    "flag"
    "log"
    "net/http"
    "os"
    "os/signal"
    "time"

    "github.com/gorilla/mux"
)

func main() {
    var wait time.Duration
    flag.DurationVar(&wait, "graceful-timeout", time.Second * 15, "the duration for which the server gracefully wait for existing connections to finish - e.g. 15s or 1m")
    flag.Parse()

    r := mux.NewRouter()
    // Add your routes as needed

    srv := &http.Server{
        Addr:         "0.0.0.0:8080",
        // Good practice to set timeouts to avoid Slowloris attacks.
        WriteTimeout: time.Second * 15,
        ReadTimeout:  time.Second * 15,
        IdleTimeout:  time.Second * 60,
        Handler: r, // Pass our instance of gorilla/mux in.
    }

    // Run our server in a goroutine so that it doesn't block.
    go func() {
        if err := srv.ListenAndServe(); err != nil {
            log.Println(err)
        }
    }()

    c := make(chan os.Signal, 1)
    // We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
    // SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught.
    signal.Notify(c, os.Interrupt)

    // Block until we receive our signal.
    <-c

    // Create a deadline to wait for.
    ctx, cancel := context.WithTimeout(context.Background(), wait)
    defer cancel()
    // Doesn't block if no connections, but will otherwise wait
    // until the timeout deadline.
    srv.Shutdown(ctx)
    // Optionally, you could run srv.Shutdown in a goroutine and block on
    // <-ctx.Done() if your application should wait for other services
    // to finalize based on context cancellation.
    log.Println("shutting down")
    os.Exit(0)
}

Middleware

Mux supports the addition of middlewares to a Router, which are executed in the order they are added if a match is found, including its subrouters. Middlewares are (typically) small pieces of code which take one request, do something with it, and pass it down to another middleware or the final handler. Some common use cases for middleware are request logging, header manipulation, or ResponseWriter hijacking.

Mux middlewares are defined using the de facto standard type:

type MiddlewareFunc func(http.Handler) http.Handler

Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed to it, and then calls the handler passed as parameter to the MiddlewareFunc. This takes advantage of closures being able access variables from the context where they are created, while retaining the signature enforced by the receivers.

A very basic middleware which logs the URI of the request being handled could be written as:

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Do stuff here
        log.Println(r.RequestURI)
        // Call the next handler, which can be another middleware in the chain, or the final handler.
        next.ServeHTTP(w, r)
    })
}

Middlewares can be added to a router using Router.Use():

r := mux.NewRouter()
r.HandleFunc("/", handler)
r.Use(loggingMiddleware)

A more complex authentication middleware, which maps session token to users, could be written as:

// Define our struct
type authenticationMiddleware struct {
	tokenUsers map[string]string
}

// Initialize it somewhere
func (amw *authenticationMiddleware) Populate() {
	amw.tokenUsers["00000000"] = "user0"
	amw.tokenUsers["aaaaaaaa"] = "userA"
	amw.tokenUsers["05f717e5"] = "randomUser"
	amw.tokenUsers["deadbeef"] = "user0"
}

// Middleware function, which will be called for each request
func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("X-Session-Token")

        if user, found := amw.tokenUsers[token]; found {
        	// We found the token in our map
        	log.Printf("Authenticated user %s\n", user)
        	// Pass down the request to the next middleware (or final handler)
        	next.ServeHTTP(w, r)
        } else {
        	// Write an error and stop the handler chain
        	http.Error(w, "Forbidden", http.StatusForbidden)
        }
    })
}
r := mux.NewRouter()
r.HandleFunc("/", handler)

amw := authenticationMiddleware{}
amw.Populate()

r.Use(amw.Middleware)

Note: The handler chain will be stopped if your middleware doesn't call next.ServeHTTP() with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. Middlewares should write to ResponseWriter if they are going to terminate the request, and they should not write to ResponseWriter if they are not going to terminate it.

Handling CORS Requests

CORSMethodMiddleware intends to make it easier to strictly set the Access-Control-Allow-Methods response header.

  • You will still need to use your own CORS handler to set the other CORS headers such as Access-Control-Allow-Origin
  • The middleware will set the Access-Control-Allow-Methods header to all the method matchers (e.g. r.Methods(http.MethodGet, http.MethodPut, http.MethodOptions) -> Access-Control-Allow-Methods: GET,PUT,OPTIONS) on a route
  • If you do not specify any methods, then:

Important: there must be an OPTIONS method matcher for the middleware to set the headers.

Here is an example of using CORSMethodMiddleware along with a custom OPTIONS handler to set all the required CORS headers:

package main

import (
	"net/http"
	"github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()

    // IMPORTANT: you must specify an OPTIONS method matcher for the middleware to set CORS headers
    r.HandleFunc("/foo", fooHandler).Methods(http.MethodGet, http.MethodPut, http.MethodPatch, http.MethodOptions)
    r.Use(mux.CORSMethodMiddleware(r))
    
    http.ListenAndServe(":8080", r)
}

func fooHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Access-Control-Allow-Origin", "*")
    if r.Method == http.MethodOptions {
        return
    }

    w.Write([]byte("foo"))
}

And an request to /foo using something like:

curl localhost:8080/foo -v

Would look like:

*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /foo HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.59.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Access-Control-Allow-Methods: GET,PUT,PATCH,OPTIONS
< Access-Control-Allow-Origin: *
< Date: Fri, 28 Jun 2019 20:13:30 GMT
< Content-Length: 3
< Content-Type: text/plain; charset=utf-8
< 
* Connection #0 to host localhost left intact
foo

Testing Handlers

Testing handlers in a Go web application is straightforward, and mux doesn't complicate this any further. Given two files: endpoints.go and endpoints_test.go, here's how we'd test an application using mux.

First, our simple HTTP handler:

// endpoints.go
package main

func HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
    // A very simple health check.
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)

    // In the future we could report back on the status of our DB, or our cache
    // (e.g. Redis) by performing a simple PING, and include them in the response.
    io.WriteString(w, `{"alive": true}`)
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/health", HealthCheckHandler)

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

Our test code:

// endpoints_test.go
package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestHealthCheckHandler(t *testing.T) {
    // Create a request to pass to our handler. We don't have any query parameters for now, so we'll
    // pass 'nil' as the third parameter.
    req, err := http.NewRequest("GET", "/health", nil)
    if err != nil {
        t.Fatal(err)
    }

    // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(HealthCheckHandler)

    // Our handlers satisfy http.Handler, so we can call their ServeHTTP method
    // directly and pass in our Request and ResponseRecorder.
    handler.ServeHTTP(rr, req)

    // Check the status code is what we expect.
    if status := rr.Code; status != http.StatusOK {
        t.Errorf("handler returned wrong status code: got %v want %v",
            status, http.StatusOK)
    }

    // Check the response body is what we expect.
    expected := `{"alive": true}`
    if rr.Body.String() != expected {
        t.Errorf("handler returned unexpected body: got %v want %v",
            rr.Body.String(), expected)
    }
}

In the case that our routes have variables, we can pass those in the request. We could write table-driven tests to test multiple possible route variables as needed.

// endpoints.go
func main() {
    r := mux.NewRouter()
    // A route with a route variable:
    r.HandleFunc("/metrics/{type}", MetricsHandler)

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

Our test file, with a table-driven test of routeVariables:

// endpoints_test.go
func TestMetricsHandler(t *testing.T) {
    tt := []struct{
        routeVariable string
        shouldPass bool
    }{
        {"goroutines", true},
        {"heap", true},
        {"counters", true},
        {"queries", true},
        {"adhadaeqm3k", false},
    }

    for _, tc := range tt {
        path := fmt.Sprintf("/metrics/%s", tc.routeVariable)
        req, err := http.NewRequest("GET", path, nil)
        if err != nil {
            t.Fatal(err)
        }

        rr := httptest.NewRecorder()
	
	// Need to create a router that we can pass the request through so that the vars will be added to the context
	router := mux.NewRouter()
        router.HandleFunc("/metrics/{type}", MetricsHandler)
        router.ServeHTTP(rr, req)

        // In this case, our MetricsHandler returns a non-200 response
        // for a route variable it doesn't know about.
        if rr.Code == http.StatusOK && !tc.shouldPass {
            t.Errorf("handler should have failed on routeVariable %s: got %v want %v",
                tc.routeVariable, rr.Code, http.StatusOK)
        }
    }
}

Full Example

Here's a complete, runnable example of a small mux based server:

package main

import (
    "net/http"
    "log"
    "github.com/gorilla/mux"
)

func YourHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Gorilla!\n"))
}

func main() {
    r := mux.NewRouter()
    // Routes consist of a path and a handler function.
    r.HandleFunc("/", YourHandler)

    // Bind to a port and pass our router in
    log.Fatal(http.ListenAndServe(":8000", r))
}

License

BSD licensed. See the LICENSE file for details.

Issues
  • PR for middleware support

    PR for middleware support

    Hello there,

    I've been using gorilla/mux for a while on an internal project at my organization, and due to some requirements, I ended up forking the project and adding some extra features, such as internal support for middleware.

    Yes, I am aware that negroni is a thing, and that it also provides this functionality. However, there are a few key things you can't do just chaining mux after your middleware:

    • Acting only if a route match is found: Waiting to see if a route actually matches before acting allows for more flexibility and coherent design. For example, an authentication middleware can happily return 403 if the required credentials are not supplied, and at the same time a normal 404 will be returned by mux if the route does not exist. This option does not exist if you need to process authentication headers before the request is matched.

    • Adding middleware for subrouters is simpler if it is embed inside mux.

    • It is more efficient. If your site receives heavy traffic and your middleware performs heavy tasks, you'll appreciate this kind of saving.

    After pondering a bit, I decided that this simple addon was worth implementing, so I did.

    As a middleware needs to be able to stop the handlers chain, I implemented it using a slightly modified http.Handler interface:

    type Middleware interface {
    	ServeHTTP(http.ResponseWriter, *http.Request) (http.ResponseWriter, *http.Request)
    }
    
    type MiddlewareFunc func(http.ResponseWriter, *http.Request) (http.ResponseWriter, *http.Request)
    

    If the middleware implementations return nil as either http.ResponseWriter or *http.Request the chain is stopped. Also, http.ResponseWriter and *http.Request can be hijacked if the middleware wants to. This is an approach slightly different to negroni's, where a third parameter of *http.HandlerFunc is added. I thought this way were better as it simplifies greatly building and iterating the middleware chain. Also, I don't like recursion. Of course, wrapper functions which receive http.Handler and http.HandlerFunc exists, so standard handlers can be chained too.

    I'm not sure if you think this feature is kind of out-of scope. But if you don't, I'll happily open a PR and discuss any modifications or style changes you'd want to be done.

    enhancement 
    opened by roobre 45
  • router.Use middleware not being hit

    router.Use middleware not being hit

    What version of Go are you running? go version go1.10.4 linux/amd64

    What version of gorilla/mux are you at? 3d80bc801bb034e17cae38591335b3b1110f1c47

    Describe your problem (and what you have tried so far) I am registering middleware, but they don't seem to be hit.

    Paste a minimal, runnable, reproduction of your issue below (use backticks to format it)

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    	"github.com/dgrijalva/jwt-go"
    	"github.com/gorilla/context"
    	"github.com/mitchellh/mapstructure"
    	"huru/migrations"
    	"huru/models"
    	"huru/models/person"
    	"huru/routes"
    	"net/http"
    	"os"
    
    	"github.com/gorilla/mux"
    	_ "github.com/lib/pq"
    	log "github.com/sirupsen/logrus"
    )
    
    func loggingMiddleware(next http.Handler) http.Handler {
    
    	log.Println("logging middleware registered");
    
    	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    		// Do stuff here
    		log.Println("Here is the request URI:",r.RequestURI)
    		// Call the next handler, which can be another middleware in the chain, or the final handler.
    		next.ServeHTTP(w, r)
    	})
    }
    
    type Exception struct {
    	Message string `json:"message"`
    }
    
    func authMiddleware(next http.Handler) http.Handler {
    
    	log.Println("auth middleware registered");
    
    	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    
    		params := r.URL.Query()
    		fmt.Println("the params are:", params);
    
    		token, _ := jwt.Parse(params["token"][0], func(token *jwt.Token) (interface{}, error) {
    			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
    				return nil, fmt.Errorf("there was an error")
    			}
    			return []byte("secret"), nil
    		})
    		claims, ok := token.Claims.(jwt.MapClaims)
    
    		if ! (ok && token.Valid) {
    			json.NewEncoder(w).Encode(Exception{Message: "Invalid authorization token"})
    			return;
    		}
    
    		var user person.Model
    		mapstructure.Decode(claims, &user)
    		context.Set(r, "logged_in_user", user)
    		next.ServeHTTP(w, r)
    	})
    }
    
    func errorMiddleware(next http.Handler) http.Handler {
    
    	log.Println("error handling middleware registered");
    
    	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    		defer func() {
    			if err := recover(); err != nil {
    
    				log.Error("Caught error in defer/recover middleware: ", err)
    				originalError := err.(struct{ OriginalError error }).OriginalError
    
    				if originalError != nil {
    					log.Error("Original error in defer/recover middleware: ", originalError)
    				}
    
    				statusCode := err.(struct{ StatusCode int }).StatusCode
    
    				if statusCode != 0 {
    					w.WriteHeader(statusCode)
    				} else {
    					w.WriteHeader(http.StatusInternalServerError)
    				}
    
    				message := err.(struct{ Message string }).Message
    
    				if message == "" {
    					message = "Unknown error message."
    				}
    
    				json.NewEncoder(w).Encode(struct {
    					ID string
    				}{
    					message,
    				})
    			}
    		}()
    		next.ServeHTTP(w, r)
    	})
    }
    
    func main() {
    
    	routerParent := mux.NewRouter()
    	routerParent.Use(loggingMiddleware)
    	routerParent.Use(errorMiddleware)
    	routerParent.Use(authMiddleware)
    
    	router := routerParent.PathPrefix("/api/v1").Subrouter();
    	router.Use(loggingMiddleware)
    	router.Use(errorMiddleware)
    	router.Use(authMiddleware)
    
    
    	// register and login
    	{
    		handler := routes.LoginHandler{}
    		subRouter := router.PathPrefix("/").Subrouter()
    		handler.Mount(subRouter, struct{}{});
    	}
    
    	{
    
    		handler := routes.RegisterHandler{}
    		subRouter := router.PathPrefix("/").Subrouter()
    		handler.Mount(subRouter, struct{}{})
    	}
    
    	{
    		// people
    		handler := routes.PersonHandler{}
    		subRouter := router.PathPrefix("/").Subrouter()
    		subRouter.Use(authMiddleware)
    		handler.Mount(subRouter, routes.PersonInjection{People: models.PersonInit()})
    	}
    }
    

    none of these get logged:

    	log.Println("error handling middleware registered");
    	log.Println("auth middleware registered");
    	log.Println("logging middleware registered");
    

    and at runtime none of middleware routes seem to get hit, nothing is logged there.

    question stale 
    opened by ORESoftware 24
  • Return a 405 when HTTP method is not supported

    Return a 405 when HTTP method is not supported

    What steps will reproduce the problem?

    1. Register a URL & handler with r.HandleFunc("/foo", myHandlerFunc).Methods("POST")
    2. Using a web browser, or cURL, hit the endpoint using an HTTP GET request

    What is the expected output? What do you see instead? Ideally, a 405 Method Not Allowed status should be returned, but the response status is a 404 File Not Found.

    (issue moved from Google Code: http://code.google.com/p/gorilla/issues/detail?id=50)

    wontfix 
    opened by moraes 24
  • mux.Vars is Empty don't understand why

    mux.Vars is Empty don't understand why

    Hello, i'm using you powerful package, it's very good, but i have a problem when i try to implement basic authentication (https://github.com/abbot/go-http-auth)

    folowing lines: htpasswd := auth.HtpasswdFileProvider(authFile) authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd)

    r := mux.NewRouter() r.HandleFunc("/licenses/{key}/status", authenticator.Wrap(apilsd.CancelLicenseStatus)).Methods("PATCH")

    func CancelLicenseStatus(w http.ResponseWriter, r *auth.AuthenticatedRequest) { vars := mux.Vars(&r.Request) fmt.Printf(" %#v", &r.Request) licenseFk := vars["key"] }

    in this case licenseFk equals empty string

    question 
    opened by Boichenko 23
  • mux/v2: need feedback

    mux/v2: need feedback

    I'd like to start a conversation and brainstorm about the future of gorilla/mux.

    Background: gorilla/mux has evolved to be the most featureful router in Go, but this came with a price: the API feels bloated and has unneeded complications. It is also far from shining in benchmarks compared to other routers (not that gorilla/mux is a bottleneck to care about, but not-so-shiny numbers give a bad impression to new users).

    I'd like to take gorilla/mux to the next level with these goals in mind:

    1. Slim API;
    2. Intuitive usage: simple rules, minimized unexpected behavior;
    3. Improved performance;

    After playing around, I came up with an implementation draft that is a radical departure from what gorilla/mux is now, and to achieve the slim-intuitive-fast goals some cuts were made:

    • Merged Path(), Handler() and HandlerFunc();
    • Merged URL(), URLHost() and URLPath();
    • Moved Scheme, Host and PathPrefix matching to the route declaration syntax;
    • Dropped duplicated Router/Route methods everywhere;

    ...but the most controversial ones are probably:

    • Dropped query, header and custom matching;
    • Dropped support for regular expressions;

    The initial result is a much slimmer mux which, besides the elemental features (scheme, host, path and method matching), offers some conveniences not easily found in other packages (subrouting, URL building), and a damn high speed (teaser: it almost beats net/http in static matching and knocks out everybody else in matching with variables; and, oh, it doesn't make a single memory allocation for any match — zero, zip, zilch, nada).

    But this is not gorilla/mux. It is something else. Maybe it doesn't even exist! Well, maybe it does. Enough teasing for now: I'd like to hear your crazy ideas, frustrations and aspirations for gorilla/mux, and to ask how could we manage an eventual API breakage in the best possible way.

    proposal 
    opened by moraes 22
  • Better error messages

    Better error messages

    I opened bug #21, which was duplicate of #6 with more information. This pull request implements most of the suggestions from there. I didn't get fancy with the header missing requests, so I just ended up using 412 regardless of the headers (some headers, per the protocol, should use different codes, it seems).

    I have a good amount of tests in there, and a couple of helper methods. I have implemented a new NotFound method handler (the default was the one from net/http, which is still used if my code doesn't find an error match).

    I saw the response from @nesv about how it would take a lot of refactoring. I kind of skirted around that by using the context to store error messages/codes that the default NotFound() handler looks for. If those context variables aren't found, it defaults to the http.NotFound handler. I thought this would be ok since this package is already tied to gorilla/context since it has the clearing of the context in ServeHTTP.

    I hope this is accepted by the project. I think this is a good way of handling custom errors, and it seems to fit in with being able to write your own matchers, as custom matchers aren't excluded from being able to set context.

    If you notice that there is some code that shows up in the diff around clearing the context, it's because I have another pull request for optionally turning off context clearing. However, I forgot to make a topic branch before I did the implementation for that facepalm. I took that logic out of the code before I started this topic branch, and I double checked that the code from that pull request was taken out, but it still shows up in the diff.

    Please let me know if there are any changes you want from me for this pull request.

    Thanks!

    enhancement documentation help wanted 
    opened by johnnadratowski 21
  • Serve React Router

    Serve React Router

    Go Version: 1.12.

    Mux-Version: latest (not important)

    Iam able to serve a react app which I created with "create-react-app", but how I serve an app which is created with "create-react-app" and has react router in it? Is this possible?

    question documentation 
    opened by Dominik-Robert 20
  • Merging PR #169 breaks existing code

    Merging PR #169 breaks existing code

    This change seems to have the very unfortunate side effect of breaking code that stores any request context data keyed by a request's address (such as gorilla/context). I have code that sets a value using gorilla/context.Set, routes the request with gorilla/mux and then expects to be able to retrieve that value in my handlers with gorilla/context.Get. With this change, the call to Get returns nil because the request address changed when setVars/setCurrentRoute is called in Router.ServeHTTP. I'm assuming it should be possible to use gorilla/mux in combination with gorilla/context, so this seems like an issue to me.

    bug 
    opened by nwidger 20
  • URL encoded parameters not working

    URL encoded parameters not working

    if you have sth like Router.HandleFunc("/bla/{foo}", someHandler)

    the "foo" parameter cant be an URL-encoded string like "hmm%2Fstrange"

    opened by philicious 20
  • URL Encoded Parameters Still Not Working in Go 1.5.1

    URL Encoded Parameters Still Not Working in Go 1.5.1

    So as a follow up to #77, I'm running Go 1.5.1 which claims to have been the cause of that issue: https://github.com/gorilla/mux/issues/77#issuecomment-101772763 and I am still seeing issues with uriEncoded paths.

    Example:

        // router build
        router = mux.NewRouter()
        router.HandleFunc("/status", status).Methods("GET")
    
        apiRouter := mux.NewRouter()
        apiRouter.HandleFunc("/foo", getFooList).Methods("GET")
        apiRouter.HandleFunc("/foo/{bar}", getFoo).Methods("GET")
    
        router.PathPrefix("/foo").Handler(apiRouter)
    
        url := httptest.NewServer(router).URL
        url1 := strings.Join([]string{url, "/foo/bar%2ftest"}, "")
        // url2 := strings.Join([]string{url, "/foo/bar.test}", "")
    
        req, _ := http.NewRequest("GET", url1, nil)
    
        client := &http.Client{}
        req, err := client.Do(req)
        fmt.Printf("Req: %+v \n Err: %+v \n", req, err)
    

    If I use url1 I get a 404, if I use url2 I get a 200. Thoughts? I am using Ember-Data to generate API calls so short of performing a huge hack on both ends which doesn't seem reasonable.

    bug 
    opened by derekdowling 19
  • URL variables bug for multiple patterns with relatively complex regexp

    URL variables bug for multiple patterns with relatively complex regexp

    Hi,

    There's a bug with URL variable with a relatively complex regexp. For example, let's take the regexp a|(b/c) which should match a or b/c.

    If I use the regexp in a path with one pattern, it works. You can test it with these routeTests:

    tests := []routeTest{
      {
        title:       "Path route with single pattern with pipe, match",
        route:       new(Route).Path("/{category:a|(b/c)}"),
        request:     newRequest("GET", "http://localhost/a"),
        vars:        map[string]string{"category": "a"},
        host:        "",
        path:        "/a",
        shouldMatch: true,
      },
      {
        title:       "Path route with single pattern with pipe, match",
        route:       new(Route).Path("/{category:a|(b/c)}"),
        request:     newRequest("GET", "http://localhost/b/c"),
        vars:        map[string]string{"category": "b/c"},
        host:        "",
        path:        "/b/c",
        shouldMatch: true,
      },
    }
    

    But if I try to use it in a path with multiple patterns, it produces weird results.

    This test...

    tests := []routeTest{
      {
        title:       "Path route with multiple patterns with pipe, match",
        route:       new(Route).Path("/{category:a|(b/c)}/{product}/{id:[0-9]+}"),
        request:     newRequest("GET", "http://localhost/a/product_name/1"),
        vars:        map[string]string{"category": "a", "product": "product_name", "id": "1"},
        host:        "",
        path:        "/a/product_name/1",
        shouldMatch: true,
      },
    }
    

    ...fails because the variables don't match:

    $ go test
    --- FAIL: TestPath (0.00 seconds)
        mux_test.go:810: (Path route with multiple patterns with pipe, match) Vars not equal:
        expected map[category:a product:product_name id:1],
        got      map[category:a product: id:product_name]
    

    And this test...

    tests := []routeTest{
      {
        title:       "Path route with multiple patterns with pipe, match",
        route:       new(Route).Path("/{category:a|(b/c)}/{product}/{id:[0-9]+}"),
        request:     newRequest("GET", "http://localhost/b/c/product_name/1"),
        vars:        map[string]string{"category": "b/c", "product": "product_name", "id": "1"},
        host:        "",
        path:        "/b/c/product_name/1",
        shouldMatch: true,
      },
    }
    

    ...also fails because the variables don't match either (but in a different way):

    $ go test
    --- FAIL: TestPath (0.00 seconds)
        mux_test.go:810: (Path route with multiple patterns with pipe, match) Vars not equal:
        expected map[category:b/c product:product_name id:1],
        got      map[category:b/c product:b/c id:product_name]
    

    EDIT: Fix manual editing mistakes (category_name instead of product_name)

    opened by xdmnl 18
  • Fix  `Single Page Application` example in `README.md` file

    Fix `Single Page Application` example in `README.md` file

    Fixes #588

    Summary of Changes

    1. Add test case to validate proposed fix (both negative and positive test case).
    2. Update README.md file.

    PS: If you want to verify how I'm able to reproduce the issue and tried my fix. Here is a link of my local PR where I have configured Github Action to execute same test cases on all the platforms i.e. ubuntu, macos and windows.

    PR link: https://github.com/amustaque97/mux/pull/1

    PS: Make sure your PR includes/updates tests! If you need help with this part, just ask!

    opened by amustaque97 1
  • Add Body Input / Output to Route [feature]

    Add Body Input / Output to Route [feature]

    Description of the solution I'd like

    How would it work? How would it change the API?

    By walking the Router we are able to Get Routes' PathTemplates, QueriesTemplates, etc. But we are not able(at least I haven't been) to get the body accepted by the request neither the possible responses' bodies. This could really come in handy when automating the writing of API Documentations as well as validating the requests received.

    By adding the functions like BodyInput( ) and BodyOutput( ) to a Route, we'd be able to set Input/Output body parameters (add Headers as well?). Therefore, by adding a function GetBodyInput() and GetBodyOutput() it'd be possible to read them. To the output nevertheless, the question to be made is : do we only set the 200 status response or do we set a map with codes as keys?

    Alternatives I've considered

    As to the alternatives, I'm trying to create an struct containing the routes, the parameters and the outputs to gaether them all in one place, but it seems to me that I'd be quite usefull to have them set on the Route itself.

    enhancement 
    opened by deBarrosS 2
  • pattern aliases

    pattern aliases

    ✋Reopened #598 because i noticed that the project came to life

    This feature introduces ability to register aliases for some often used regular expressions.

    For example path as this: /{category:[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}}/{product:[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}} can be transformed into this /{category:uuid}/{product:uuid}

    You need just use register it

    router := NewRouter()
    router.RegisterPattern("uuid", "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}")
    router.Path("/{category:uuid}")
    router.Path("/{product:uuid}")
    router.Path("/{order:uuid}")
    router.Path("/{user:uuid}")
    
    1. New method RegisterPattern on Route struct
    2. New method RegisterPattern on Router struct
    opened by HiveTraum 3
  • fix(mux.go): use cleaned path as URL.Path

    fix(mux.go): use cleaned path as URL.Path

    Fixes #673

    Summary of Changes

    1. removed the redirection on path mismatch after cleanup
    2. used the cleaned up path for finding Path match

    PS: Make sure your PR includes/updates tests! If you need help with this part, just ask! Will Update tests if the approach looks good

    opened by with-shrey 8
Releases(v1.8.0)
  • v1.8.0(Aug 22, 2020)

    v1.8.0 addresses a minor regression with the variable extraction when using a wildcard host match. Specifying .Host(“foo.example.com:{port:.*}) will now correctly capture the port into mux.Vars.

    CHANGELOG

    • fix: regression in vars extract for wildcard host (#579) @harshavardhana
    • Remove stale text from comment. (#568) @EricIO
    Source code(tar.gz)
    Source code(zip)
  • v1.7.4(Feb 12, 2020)

    v1.7.4 addresses a number of performance improvements (#544), bugs (#509, #535), and documentation improvements.

    CHANGELOG

    • Reduce allocations in (*routeRegexp).getURLQuery (#544) @klauspost
    • Fixed golint warnings (#526) @KosToZyB
    • fix headers regexp test case (#536) @icattlecoder
    • Fix the CORSMethodMiddleware bug with subrouters (#535) @fharding1
    • Remove/cleanup request context helpers (#525) @fharding1
    • Guess the scheme if r.URL.Scheme is unset (#474) @euank
    • Added capacity to slice creation, when capacity is known (#516) @vivekv96
    • Modified http status codes, using constants provided by the http package (#514) @vivekv96
    • bugfix/subrouter custom methodNotAllowed handler returning 404 (#509) (#510) @jonasdebeukelaer
    • Update README (self-host logo) (#513) @elithrar
    • Remove TravisCI badge (#503) @elithrar
    • Add documentation for using mux to serve a SPA (#493) @fharding1
    • Simplify code (#501) @muesli
    • Avoid unnecessary conversion (#502) @muesli
    • Update config.yml (#495) @elithrar
    Source code(tar.gz)
    Source code(zip)
  • v1.7.3(Jun 30, 2019)

    This is a patch release that mostly incorporates test & build system updates, as well as fixing a rare cause of panics.

    CHANGELOG

    • Update stale.yml (#494) @elithrar
    • Improve CORS Method Middleware (#477) @fharding1
    • Delete ISSUE_TEMPLATE.md (#492) @elithrar
    • Use subtests for middleware tests (#478) @fharding1
    • Delete .travis.yml (#490) @elithrar
    • Fix nil panic in authentication middleware example (#489) @fharding1
    • [WIP] Create CircleCI config (#484) @elithrar
    Source code(tar.gz)
    Source code(zip)
  • v1.7.2(May 17, 2019)

    ⚠️ This release relaxes the host:port matching when using *Route.Host() as introduced in v1.7.0. If a port is not provided, the route will match on any port, matching behaviour for all versions of mux prior to v1.7.0.

    See #447 for a fuller discussion.

    CHANGELOG

    • Route.Host -matching will ignore any provided port from getHost(), if… (#447) @cognusion
    Source code(tar.gz)
    Source code(zip)
  • v1.7.1(Apr 5, 2019)

    No notable changes 🎆

    CHANGELOG

    • Minor spelling mistake (#454) @souvikhaldar
    • fix go1.12 go vet usage (#458) @seriousben
    • adding Router.Name to create new Route (#457) @seriousben
    • Remove sudo setting from travis.yml (#462) @sekky0905
    Source code(tar.gz)
    Source code(zip)
  • v1.7.0(Jan 27, 2019)

    Notable changes:

    • mux will only support Go 1.7 and newer: this has allowed us to remove the older gorilla/context code. Go 1.7 has been out since August 2016.
    • *Route.Host() matching now includes the host:port, as opposed to just the host.
    • Subrouters should correctly return a 405 if the route matches but the method does not (see #437 for details)

    CHANGELOG

    • [deps] Add go.mod for versioned Go (#376) @elithrar
    • Initialize user map (#371) @kalafut
    • Update LICENSE & AUTHORS files. (#386) @elithrar
    • Add 1.11 to build in travis (#398) @yershalom
    • Drop support for Go < 1.7: remove gorilla/context (#391) @fharding1
    • Create release-drafter.yml (#399) @elithrar
    • Always run on the latest stable Go version. (#402) @elithrar
    • Use constant for 301 status code in regexp.go (#412) @trungnn
    • Use subtests in mux_test.go (#415) @kanozec
    • Add stalebot config (#424) @elithrar
    • Update and rename stale to stale.yml (#425) @elithrar
    • remove redundant code that remove support gorilla/context (#427) @alimy
    • Replace domain.com with example.com (#434) @raeesbhatti
    • fix #2, return full host:port info from getHost (#383) @santsai
    • Ignore ErrNotFound while matching Subrouters (#438) @g-w
    • Call WriteHeader after setting other header(s) in the example (#442) @timucingelici
    • replace rr.HeaderMap by rr.Header() (#443) @moeryomenko
    Source code(tar.gz)
    Source code(zip)
  • v1.6.2(May 13, 2018)

    Adds the CORSMethodMiddleware middleware, which allows package users to automatically generate a Access-Control-Allow-Methods HTTP response header based on which methods are configured for a given route. This is useful when dealing with cross-origin requests, and can be easily enabled via router.Use(mux.CORSMethodMiddleware).

    This release otherwise includes documentation fixes and improvements. We are always looking to improve the documentation for mux, and contributions are welcome!

    CHANGELOG e3702be [docs] Improve docstrings for middleware, skipclean (#375) fdeb7bc [docs] Doc fix for testing variables in path (#374) 5e55a4a Add CORSMethodMiddleware (#366) ded0c29 Fix linter issues (docs) (#370) b57cb16 [build] Update Go versions; add 1.10.x (#364) 94231ff Fix table-driven example documentation (#363) 4dbd923 Make Use() variadic (#355) 07ba1fd Modify http status code to variable in README (#350) d284fd8 Modify 403 status code to const variable (#349)

    Source code(tar.gz)
    Source code(zip)
  • v1.6.1(Jan 16, 2018)

    gorilla/mux now provides first-class support for "middleware" thanks to @roobre! Middleware are handlers that can intercept request & response processing after a route match, but before/after other handlers are invoked. See the documentation for the full details: https://github.com/gorilla/mux#middleware

    Previously, developers would have to use a thin wrapper to achieve this per-application. Requests for this functionality built up enough over time such that we've added it to mux's built-in methods.

    CHANGELOG:

    53c1911 [feat] Add middleware support as discussed in #293 (#294) 5bbbb5b [docs] Add graceful shutdown example (#329) 512169e refactor routeRegexp, particularily newRouteRegexp. (#328) 5ab525f Public test API to set URL params (#322) 7904d2e [docs] Add example usage for Route.HeadersRegexp (#320) c572efe [docs] Note StrictSlash re-direct behaviour #308 (#321) 65ec724 Create ISSUE_TEMPLATE.md (#318) 4a3d4f3 [bugfix] Fix method subrouter handler matching (#300) (#317) 2d5fef0 [docs] fix outdated UseEncodedPath method docs (#314)

    Source code(tar.gz)
    Source code(zip)
  • v1.6.0(Nov 7, 2017)

    v1.6.0 major changes:

    • RouteMatch.MatchErr is now set to ErrNotFound when the route does not match.
    • Go 1.5.x and below are no longer supported
    • New methods for fetching the query template have been added in Route.GetQueriesRegexp and Route.GetQueriesTemplates
    • Bug fixes and documentation updates, detailed below.

    CHANGELOG 7f08801 MatchErr is set to ErrNotFound if NotFoundHandler is used (#311) 9f48112 [docs] Document router.Match (#313) bc452d9 [build] Allow tip failures (#312) 7625a85 .travis.yml: Remove versions < go1.5 from build matrix c9183aa use req.URL.EscapedPath() instead of getPath(req) (#306) 10490f5 GetQueryTemplates and GetQueryRegexp extraction (#304) 9bd9ff2 Added 1.9 build step (#303) bdd5a5a Fix WriteHeader in TestA301ResponseWriter. (#301) 3f19343 [docs] Document evaluation order for routes (#297)

    Source code(tar.gz)
    Source code(zip)
  • v1.5.0(Sep 12, 2017)

    v1.5.0 major changes:

    • A HTTP 405 "Method Not Allowed" will be returned when the method does not match for the route. This is a change from existing behaviour, where a 404 is returned instead. This behaviour, and the error message, can be customized by setting the MethodNotAllowed property of a *Router instance.
    • Walk will now walk all attached sub-routers

    CHANGELOG

    24fca30 [docs] README.md: add missing . (#292) bb285ea [docs] Fix missing space in docstring (#289) a659b61 Fix #271: Return 405 instead of 404 when request method doesn't match the route ac112f7 Prefer scheme on child route when building URLs. 37b3a6c Use scheme from parent router when building URLs. 8c683ee Fix typo 18fca31 Add test and fix for escaped query values. c7a138d Update docs. f9aa23a Add tests for support for queries in URL reversing. 9c9af15 Add support for queries in URL reversing. 043ee65 Update Walking Routes Section 59ce668 Fix invalid example code 85b8c20 Removing half of conflict marker (#268) 456bcfa Update README with example for Router.Walk 4d814f7 Update ancestors parameter for WalkFunc for matcher subrouters a322b2c Update Walk to match all subrouters

    Source code(tar.gz)
    Source code(zip)
  • v1.4.0(May 21, 2017)

    bcd8bc7 Support building URLs with non-http schemes. (#260) b552615 Added method Route.GetMethods 1856953 Added method Route.GetPathRegexp 599cba5 Fixing Regexp in the benchmark test (#234) ad4ce0e updating logic in route matcher, cleaner and saner (#235)

    Source code(tar.gz)
    Source code(zip)
  • v1.3.0(Jan 18, 2017)

    Summary

    • Capture groups in regular expressions - e.g. (pattern|thing) vs (?:pattern|thing) are have not been supported. We now fail fast (panic on startup) if we detect any routes with a capturing group. See https://github.com/gorilla/mux/issues/200 for discussion.

    CHANGELOG

    392c28f [bugfix] fail fast if regex is incorrectly specified using capturing groups. (#218) cafdb65 [docs] Add route listing example to README

    Source code(tar.gz)
    Source code(zip)
  • v1.2.0(Jan 17, 2017)

    Summary:

    • PathPrefix now supports defining an index path by calling PathPrefix("/foo") and defining a route as /
    • Documented the regex support - capture groups should not be provided.
    • Fix encoded path & variable extraction bugs.

    CHANGELOG:

    b128961 Merge pull request #199 from wirehead/minor-doc-tweek 34dda71 Merge pull request #215 from ShaneSaww/fix_for_subroutes_with_pathPrefix b9ff34f Adding some extra tests, to hit all the use cases 293ebe1 Adding in a check for routes with just / 910dd3a Remove unnecessary line from example 239e05f Clarify how route variables work. (#151) 757bef9 Merge pull request #196 from olt/doc-non-capture-groups 14f5df0 document non-capturing groups (closes #143) 0a192a1 Add useEncodedPath option to router and routes (#190) 0b13a92 Simplify extractVars, fixes edge cases. (#185)

    Source code(tar.gz)
    Source code(zip)
  • v1.1(Mar 26, 2016)

    We're now tagging releases with SemVer, something that was long overdue. gorilla/mux has strict API compatibility guarantees, excepting any security issues, but we understand that being able to vendor specific versions is a useful feature.

    Minor breaking changes (i.e. regex functionality) will be treated as minor releases.

    We also strongly recommend that you stay up to date: although this project has not had any security issues to date, back-porting security patches across multiple releases (v1.1.1, v1.2.1, etc.) is not maintianable for us.

    Source code(tar.gz)
    Source code(zip)
Owner
Gorilla Web Toolkit
Gorilla is a web toolkit for the Go programming language that provides useful, composable packages for writing HTTP-based applications.
Gorilla Web Toolkit
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 438 Jun 15, 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 267 Jun 13, 2022
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

徐佳军 525 May 29, 2022
Go HTTP request router and web framework benchmark

Go HTTP Router Benchmark This benchmark suite aims to compare the performance of HTTP request routers for Go by implementing the routing structure of

Peter Kieltyka 23 Apr 21, 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 Apr 10, 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 387 Jun 11, 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 251 May 30, 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 441 Jun 25, 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 Mar 3, 2022
Lightweight Router for Golang using net/http standard library with custom route parsing, handler and context.

Go-Lightweight-Router Lightweight Router for Golang using net/http standard library with custom route parsing, handler and context. Further developmen

null 0 Nov 3, 2021
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 53 Jan 24, 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 128 Jun 13, 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.2k Jun 26, 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 543 Jun 20, 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 126 Jun 25, 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 105 Mar 21, 2022
Simple router build on `net/http` supports custom middleWare.

XMUS-ROUTER Fast lightweight router build on net/http supports delegate and in url params. usage : Create new router using NewRouter() which need rout

amupxm [amir hossein mokarrami far] 5 Dec 27, 2021
Simple HTTP router for Go

ngamux Simple HTTP router for Go Installation Examples Todo Installation Run this command with correctly configured Go toolchain. go get github.com/ng

null 51 Jun 1, 2022