Giter Club home page Giter Club logo

mux's Introduction

gorilla/mux

testing codecov godoc sourcegraph

Gorilla Logo

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) {
	// Join internally call path.Clean to prevent directory traversal
	path := filepath.Join(h.staticPath, r.URL.Path)

	// check whether a file exists or is a directory at the given path
	fi, err := os.Stat(path)
	if os.IsNotExist(err) || fi.IsDir() {
		// file does not exist or path is a directory, serve index.html
		http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
		return
	}

	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 file
	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")

To find all the required variables for a given route when calling URL(), the method GetVarNames() is available:

r := mux.NewRouter()
r.Host("{domain}").
    Path("/{group}/{item_id}").
    Queries("some_data1", "{some_data1}").
    Queries("some_data2", "{some_data2}").
    Name("article")

// Will print [domain group item_id some_data1 some_data2] <nil>
fmt.Println(r.Get("article").GetVarNames())

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{tokenUsers: make(map[string]string)}
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()
	
	// To add the varsย to the context, 
	// we need to createย a router through which we canย pass the request.
	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.

mux's People

Contributors

bgaifullin avatar bign8 avatar blasteralex avatar brocaar avatar burrbd avatar chrishines avatar coreydaley avatar djgilcrease avatar dmitshur avatar ejholmes avatar elithrar avatar fharding1 avatar kimmachinegun avatar kisielk avatar kushmansingh avatar moraes avatar mtso avatar muesli avatar nickhudkins avatar nmiyake avatar owenthereal avatar pschlump avatar roobre avatar seriousben avatar shanesaww avatar shkw avatar sqs avatar ttencate avatar tumdum avatar vivekv96 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mux's Issues

Debugging output

It would be really helpful if I could set a debug flag on a router and have it spit out log messages telling me what it's doing when it's asking to handle a request. As it is, debugging routing failures requires a lot of guess work.

I'd be happy to work on a PR if you could give me some guidance on the API.

Attach subrouter to router.

Hi,

I am coming from Martini and with Martini you can handle subrouters like so,

package main

import (
    "github.com/go-martini/martini"
    "net/http"
)

func main() {
    m := martini.Classic()
    m.Group("/user", users.GetRouter)
    m.Group("/posts", posts.GetRouter)

    http.Handle("/", m)
}
package users
func GetRouter(r martini.Router) {
    r.Get("/{id}", GetBooks)
    r.Post("/new", NewBook)
    r.Put("/update/{id}", UpdateBook)
    r.Delete("/delete/{id}", DeleteBook)
}


func GetBooks(res http.ResponseWriter, req *http.Request) {
 //DO stuff here.
})
package posts
func GetRouter(r martini.Router) {
    r.Get("/{id}", GetPosts)
    r.Post("/new", NewPost)
..
..
..

How can I achieve the same structure with Gorilla Mux? I did read through the Gorilla Mux's GoDocs and I can't seem to find a way to handle this.

The problem I have is that it seems like you can only create subrouters for a parent route with Gorilla Mux, so I was considering this solution:

package main

import (
    "code.google.com/p/gorilla/mux"
    "fmt"
    "net/http"
)

func main() {

    r := mux.NewRouter()

    users.SetRoutes(r.PathPrefix("/users").Subrouter())
    posts.SetRoutes(r.PathPrefix("/posts").Subrouter())

    http.Handle("/", r)

}
package users

import (
    "code.google.com/p/gorilla/mux"
)

func SetRoutes(r mux.Router) {
    r.HandleFunc("/{id}", GetBook)
}


func GetBook(res http.ResponseWriter, req *http.Request) {
 //DO stuff here.
})
package posts

import (
    "code.google.com/p/gorilla/mux"
)

func SetRoutes(r mux.Router) {
    r.HandleFunc("/{id}", GetPost)
}

.

But that is not really clean and it looks backwards in the sense that the subpackage is consuming the main package, instead of the other way around.
I was hopping for solution like this:

func main() {

    r := mux.NewRouter()

    r.PathPrefix("/users").Attach(users.GetRoutes)
    r.PathPrefix("/posts").Attach(posts.GetRoutes)

    http.Handle("/", r)

}
package users

import (
    "code.google.com/p/gorilla/mux"
)

func GetRoutes(r mux.Router)  {
    r.HandleFunc("/{id}", GetBook)
}


func GetBook(res http.ResponseWriter, req *http.Request) {
 //DO stuff here.
})
package posts

import (
    "code.google.com/p/gorilla/mux"
)

func GetRoutes(r mux.Router ) {

    r.HandleFunc("/{id}", GetPost)
}

deleting routes

Last night I read all of the stdlib net/http code in order to determine whether or not it was possible to delete a route once it was added and it seems that while you can add handlers at runtime you cannot delete them. Looking at the Gorilla Mux documentation I see much of the same. Is there any particular use-case against being able to delete routes?

Is mux supposed to work with WebSocket?

Hi,

I am using mux together with WebSocket in my app and I am constantly getting 404. When I replace mux with http.ServeMux, everything starts working. I register the handler with

r.Handle("/ws", ws.Handler(s.handleWSConnection))

and

mx := http.NewServeMux()
mx.Handle("/", r)
mx.Handle("/ws", ws.Handler(s.handleWSConnection))

is the working code.

I am asking because I saw people using it on the internet, so I don't know if it broke recently or so...

Thanks for the reply!

Regards,
Ondra Kupka

Problem parsing % as a pattern in URL

Having defined a mux Handler as:
r.HandleFunc("/info/{name:[A-Za-z0-9._%-]+}/", getInfo).Methods("GET")

It doesn't handle URLs containing any percentage symbol. For example I get:
GET http://127.0.0.1:8081/info/sample%20name/ 404 (Not Found)

Is there a way of defining a pattern for parsing % symbols or spaces coded as %20?

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)

DELETE and PUT methods don't work with some browsers

When some browsers (i.e. current version of chrome and firefox) are making Delete or Put requests via ajax, they actually make the METHOD=POST and change the header to "HTTP_X_HTTP_METHOD" to "DELETE" or "PUT".

When this occurs, mux fails to properly detect a DELETE or PUT request.

Version tags

Hi, would you mind create version tag (0.1 or even 1.0)? So we can use gopkg.in versioning system for installing stable(or though reproducible) mux versions. Also it can be good for all gorilla packages.
Thank you.

Document Usage Of Router.NotFoundHandler

Right now on the website there isn't any documentation on how to handle 404 errors. Documentation should be added for this, because the functionality is included in the library and I'm sure a lot of people will use it! :)

Thank you,

Evan

Header constraint not working

Hi everyone!

I'm developing a small webserver and I've tried to limit access only to Cloudfront, by ensuring the header 'User-Agent' is set to 'Amazon Cloudfront'. I know it's not the safest thing in the world, but at least avoids unintentional requests.

However, I haven't managed to apply this constraint. This is my code so far:

r := mux.NewRouter()

// Constraints
s := r.Headers("User-Agent", "Amazon Cloudfront").PathPrefix("/{connection_id}").Subrouter()

// Routes
s.HandleFunc("/product-images/{model_id}", ProductImagesHandler)
s.HandleFunc("/product-images/{model_id}/{max_dimension:[0-9]+}", ProductImagesHandler)
s.HandleFunc("/nav-item-images/{model_id}", NavItemImagesHandler)
s.HandleFunc("/nav-item-images/{model_id}/{max_dimension:[0-9]+}", NavItemImagesHandler)
http.Handle("/", s)

// Keep listening
http.ListenAndServe(":3000", s)

However, all the requests go through. Is this a bug? Am I missing something?

I'm using latest Go (1.4.2).

Thanks in advance! ;)

matching headers - accept

I've been trying to match a header:

r := mux.NewRouter()
r.HandleFunc("/getdata", getdata_handler).
    Host("localhost").
    Methods("GET").
    Headers("Accept", "text/json")

The source of the transaction is a jQuery(1.9.1) $.ajax() call.

$.ajax({
    url: 'http://localhost:3000/getdata?callback=?',
    type: 'GET',
    timeout: 10*1000,
    accepts :{json:"text/json", jsonp:"text/json"},
    jsonp: 'callback',
    dataType: 'jsonp',
    success: function(data) {
        console.log(data);
    },
    error: function(  jqXHR,  textStatus,  errorThrown) { console.log('error', textStatus, errorThrown); },
});

I have been playing with ?headers?, ?accept? and ?sendBefore?. Debugging this has been nearly impossibly. But after reviewing the gorilla code and reading this comment:

"Funny enough, even doing this the Accept header you will send will be something like "application/json, /; q=0.01", so take care on the server."
-- (http://forum.jquery.com/topic/ajax-accepts-setting-not-creating-accept-header)

The Match() code is looking for a string match instead of a substring match. Take a loog at the expressjs code. My recollection from the doc is that they do some fuzzy matching instead of exact.

Can't use path variables in subrouter

In the following application I expected a request to /foo/bar/11 and /foo/11 to both return the same response; Vars: map[id:11]. But it turns out that /foo/bar/11 instead returns a 404 error.

package main

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

func Test(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Vars: %v", mux.Vars(r))
}

func main() {
    root := mux.NewRouter()
    foo := root.Path("/foo/").Subrouter()
    foobar := foo.Path("/bar/").Subrouter()

    foo.HandleFunc("/{id}", Test).Name("foo")
    foobar.HandleFunc("/{id}", Test).Name("bar")

    http.Handle("/", foo)

    http.ListenAndServe(":8080", nil)
}

API compatibility with http.ServeMux

The API of mux#Router.HandleFunc is slightly different which makes it really hard to write libraries which can use both github.com/gorilla/mux Router and http.ServeMux.

cannot use r.PathPrefix("/api/v1/").Subrouter() (type *mux.Router) as type sleepy.APIMux in function argument:
*mux.Router does not implement sleepy.APIMux (wrong type for HandleFunc method)
have HandleFunc(string, func(http.ResponseWriter, *http.Request)) *mux.Route
want HandleFunc(string, func(http.ResponseWriter, *http.Request))

So the difference is the return value of HandleFunc. I see no real value of returning the Router again. What are the reasons - i suggest that the return value is removed to get API compatibility.

Schemes() if not matching my test

// this code does not work

r := mux.NewRouter()
r.HandleFunc("/getdata", getdata_handler).
    Host("localhost").
    Methods("GET").
    Headers("Accept", "text/json").
    Schemes("http").
    Name("getdata")

// this code works

r := mux.NewRouter()
r.HandleFunc("/getdata", getdata_handler).
    Host("localhost").
    Methods("GET").
    Headers("Accept", "text/json").
    Name("getdata")

Here is the test:

        $.ajax({
            url: 'http://localhost:3000/getdata?callback=?',
            //headers: { "Accept": "text/json" },
            type: 'GET',
            dataType: 'json',
            jsonp: 'callback',
            success: function(data) {
                console.log(data);
                template = $('#tabletemplate').clone().html();
                console.log(template);
                snippet = Mustache.render(template, data);
                console.log(snippet);
                $('#workspace').empty().append(snippet);
            },
            error: function(  jqXHR,  textStatus,  errorThrown) { console.log('error', textStatus, errorThrown); },
            beforeSend: function(jqXHR){ jqXHR.setRequestHeader("Accept", "text/json"); }
        });

Response redirect drops query string

If you have http://localhost//api/status?abc=def and you have a handler for /api/status
the code in mux.go will do a redirect to http://localhost/api/status and drop the ?abc=def. This is the same as issue http://code.google.com/p/go/issues/detail?id=5252, fixed in go 1.2 rc 4. I am trying to figure out how to do a pull requet as
I have fixed and tested this. You can look at my fork of mux.go lines 70 to 77.
https://github.com/pschlump/mux.git Code is modified from the 1.2rc4 fix.

Document how to make custom matcher functions

Hey,

As far as I can see, despite great documentation in general there's nothing on how to make custom matcher functions or anything on the RouteMatch struct. Does that exist somewhere, and if not, could someone either explain to me how they work so that I can add documentation or just add material to the docs and let me know?

Thanks,

mux headers do not support regex

since accept headers can be complex with all sorts of version information embedded it would be better if it supported regex in the value portion of the pair.

Host() with a port fails to match when the host is supplied in the HTTP Host: header

Old issue: http://code.google.com/p/gorilla/issues/detail?id=51

According to section 14.23 of RFC 2616 the Host header can include the port number if the default value of 80 is not used.

See https://github.com/gorilla/mux/blob/master/mux_test.go#L119 for a commented-out test case that fails.

Potential fix is editing getHost() https://github.com/gorilla/mux/blob/master/regexp.go#L237 so it has a flag as to whether to strip off the port information. A new field would need to be probably need to be added somewhere such as routeRegexp.

The main reason I was using Host() match with a port in the first place is for trying to build reverse routes that include the absolute path as opposed to a relative one.

Route.URL creates incorrect URL when PathPrefix is in use

Maybe I'm misunderstanding but if I have a Route with a defined path prefix, on making use of Route.URL the prefix is appended, rather than prepended. Sample code:

package main

import (
    "fmt"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    route := r.Path("/testpath").PathPrefix("/prefix/")
    url, _ := route.URL()
    fmt.Println(url)
}

The Println outputs /testpath/prefix/

Having trouble importing mux: package appears to have no public members

I have a very simple go file, and when I try to build, i get this:

go build
# _/home/matt/projects/goprojects/muxHello
./main.go:10: undefined: mux.HandleFunc
./main.go:14: undefined: mux.DefaultRoute

here is my code:

package main

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

func main() {
    mux.HandleFunc("/", func(w http.ResponseWriter, r http.Request) {
        fmt.Fprint(w, "Hello")
    })

    http.Handle("/", mux.DefaultRoute)
    http.ListenAndServe(":10000", nil)
}

I set my GOPATH to /home/matt/projects/goprojects/muxHello
then do

go get github.com/gorilla/mux

and the folders show up in

/home/matt/projects/goprojects/muxHello/src/github.com/gorilla/mux
/home/matt/projects/goprojects/muxHello/src/github.com/gorilla/context

I've tried other imports and they have worked. for instance, https://code.google.com/p/goauth2/ and https://github.com/eaigner/jet both work using the exact same steps I took with gorilla/mux.

It's like it is finding the package; but the package isn't exporting anything.

here is the output of "go env"

GOARCH="amd64"
GOBIN=""
GOCHAR="6"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/matt/projects/goprojects/muxHello"
GORACE=""
GOROOT="/home/matt/go"
GOTOOLDIR="/home/matt/go/pkg/tool/linux_amd64"
CC="gcc"
GOGCCFLAGS="-g -O2 -fPIC -m64 -pthread"
CGO_ENABLED="1"

I installed go from source. I used the release branch of go 1.1.

 go version go1.1 linux/amd64

I am building a restful api as my first go project. I apologize in advance if this is because of a mistake I've made or if this isn't the right "forum" for this. I have spent several hours trying to make sure everything is setup correctly. I don't know what else to do. I created an issue because I feel like I've done everything correctly, I followed the directions on the gorilla/mux site and I haven't had this issue with other libraries.

Gracefull shutdown

Hi there, I'm kind of a noob in go so may be I'm asking this in the wrong place.

Is there someway to tell the mux to shutdown gracefully ?

May be I could use manners or graceful as a proxy ?

Site down?

I'm trying to access the site in order to take a look at the documentation and it's giving me a 500 error.

Regexp parentheses matching overwrites mux-defined keys

NOTE: This may be similar / duplicate to issue #11.

Use case - avoiding duplication of

r.HandleFunc("/user/view/{id:[0-9]+}", user.GetViewPage).Methods("GET")
r.HandleFunc("/user/{id:[0-9]+}", user.GetViewPage).Methods("GET")

With something like

r.HandleFunc("/user/{_:(view/)?}{id:[0-9]+}", user.GetViewPage).Methods("GET")

Results:

/user/5
2014/03/15 12:20:47 map[_: id:]
/user/view/5
2014/03/15 12:20:39 map[_:view/ id:view/]

As you can see, the regex matching view/ is somehow being assigned to id. The docs state that name:pattern matches a regexp pattern - as you can see above, this does not seem to be occurring properly.

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)

Catch root of subrouter

The route below matches /

router := mux.NewRouter()
router.Methods("GET").Path("/").HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
    res.Write([]byte("/"))
})

If I want to match /subroute, shouldn't I be able to use this? It currently does not seem to work. Is this by design or a mistake?

subrouter := router.PathPrefix("/subroute").Subrouter()
subrouter.Methods("GET").Path("/").HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
    res.Write([]byte("/subroute"))
})

project layout in github

when you look at the sum of the gorilla project code it's not all that much, however, having separate repos for each subproject feels less than optimum. In the coming days I plan to fork the code so I can make some changes, however, I expect some of the dependencies to get in the way and so hacking those are going to make the task of pulling changesets more difficult. This is just my intuition so feel free to tell me I'm wrong.

mux.Vars is empty - and shoudn't be

I bet it's just me doing something wrong - but then I would ask to improve the docs.
Here is what I am trying:

router := mux.NewRouter()
http.Handle("/", handler.LoggingInterceptor(handler.AuthenticationInterceptor(router)))
router.HandleFunc("/project/{project:[0-9a-zA-Z]+}", handler.PutProject).Methods("PUT")

With the interceptors along the lines of:

func AuthenticationInterceptor(router http.Handler) http.Handler {
  return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
    vars := mux.Vars(req)
    glog.Infof("Vars: [%q] but %s", vars, req.URL.Path)
    router.ServeHTTP(res, req)
  })
}

and

func LoggingInterceptor(router http.Handler) http.Handler {
  return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
    router.ServeHTTP(res, req)
  })
}

Pretty straight forward. But somehow the variables are empty inside the interceptor.

Vars: [map[]] but /project/test

As you can see from the logs the request does get matched correctly and the request uri looks OK - but there is no var "project".

What am I missing here?

getNamedRoutes not exported

For debugging purposes it would be nice to get all routes, but the getNamedRoutes function is not exported. Is there any reason for that?

Queries() matches against non-exactly-matching queries.

At the master branch, (r *Router) Queries(pairs ...string) looks have a probmems.
It matches querystrings which are not exactly matched.

When you set r.Queries("foo","bar"), this router matches queries like "fffoo=barrrrrr".
I think that any problem is in Regexp.

Following test fails accidently.

    {                                                                                                                                                                                                                                                                         
      title:       "Queries route, bad submatch",                                                                                                                                                                                                                             
      route:       new(Route).Queries("foo", "bar", "baz", "ding"),                                                                                                                                                                                                           
      request:     newRequest("GET", "http://localhost?fffoo=bar&baz=dingggg"),                                                                                                                                                                                               
      vars:        map[string]string{},                                                                                                                                                                                                                                       
      host:        "",                                                                                                                                                                                                                                                        
      path:        "",                                                                                                                                                                                                                                                        
      shouldMatch: false,                                                                                                                                                                                                                                                     
    },

StrictSlash Redirect Still Executes Original Controller

I'm not sure how yet, but if I have a router with StrictSlash turned on, and I send a request to, say

/some/path

it will correctly redirect to

/some/path/

but in the process it also seems to call the original handler bound to the route. I'm planning on investigating some more tomorrow and seeing if I can figure out what's causing this, or at least get you a minimal repro example, but before I start in on that I'd appreciate it if you could confirm for me that it's not actually intended behavior?

how to bind 2 URLs on 1 function

Hi,

I want to bind 2 URLs to 1 function. like below.

router.HandleFunc("/_Users", controller.AddResource)
router.HandleFunc("/Resources/{resourceName}", controller.AddResource)

the controller.AddResource is a global function. the code like below:

function AddResource(req *http.Request, w http.ResponseWriter){
    vars := mux.Vars(req)
    resourceName := vars["resourceName"]
    data = request.Body
    AddDataToResource(resourceName, data)
}

so how can i set the vars(resourceName='_User') in the first router?

thanks...

Subroutes using PathPrefix don't redirect on POST

Looking at #31 looks like it's a similar issue with POST though.

Using

bookRoute := router.PathPrefix("/book").Subrouter()
bookRoute.Handle("/", createBookHandler).Methods("POST")

Will only POST to /book/ and not /book without the trailing slash
Trying to append StrictSlash(true) to the Subrouter() has no effect.

Is there something I'm missing in the setup?

In addition, maybe this is stupid here, but when I POST to the GET route it responds properly. So the following would both succeed, but obviously supports GET requests as well.

bookRoute := router.PathPrefix("/book").Subrouter()
get := router.Methods("GET").Subrouter()

get.Handle("/book", createBookHandler) // Handles both GET and POST
bookRoute.Handle("/", createBookHandler).Methods("POST")

If I remove the bookRoute there would only be a 404.

If a route is matched by everything except the method, router should return a 405 listing the available methods

I have some routes that I only allow certain methods for. When I try another method for the same URI, I get a 404. As per the HTTP 1.1 spec, I should get a 405 with the proper Allow header listing the allowed methods.

Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.6

UPDATE:
I also noticed that seemingly unexpected behavior occurs when routes don't match on headers or on queries. On queries I'd think you'd want to return a 400 with some information about how certain queries aren't allowed. On headers it might be harder as it seems depending on the header you might want to return a 406, 412, 415, 416, or a 417? I'm not sure with that one.

Feedback is appreciated. When I get some time, if you guys like the idea, I might make a pull request and do it myself.

cleanPath() causing a change in the URL should not necessarily cause a redirect

Say I have an API with a URL structure as follows: http://example.com/library/$BOOKID/chapters

If someone hits the URL http://example.com/library//chapters (Fails to provide a bookID), the client will receive a 301 to a location which doesn't exist (301 -> follow -> 404)

In my opinion, this behaviour should be optional, the problem is of course that path.Clean() is called, and (As per documentation) // 1. Replace multiple slashes with a single slash. is performed.

Most my reasoning for not wanting this behaviour is based on me not wanting my routing library to redirect my clients when I'm not explicitly asking it to.

What are others thoughts on this?

Cheers,

Optionally Match trailing slash in the URL and HTTP Method based matching

Lets say we have a URL like

http://www.hostname/upload/

Considering that the user types in

http://www.hostname/upload (note no trailing slash)

Possibly we can redirect the user from the no-slash one to the slashed one by registering different handlers.StrictSlash does this. But is there a way to match both of them?

we use mux like this

package main
import (
       "fmt"
       "github.com/gorilla/mux"
       "net/http"
)
func uploadHandler(w http.ResponseWriter, r * Request){
        fmt.Fprintf(w,"Upload page")
}

func main(){
    routes := mux.NewRouter()
    routes.HandleFunc("/upload",uploadHandler) //this doesnot match the trailing slash case
    http.Handle("/",routes)
    http.ListenAndServe("0.0.0.0:8081",nil)
}

How do we write the matching condition so that it accepts both http://www.hostname/upload/ and http://www.hostname/upload ?

Another question. How do we execute different handlers based on the request method?

e.g., the GET request to upload can be handled by different handler and POST can be handled by another function.

I already know that we can check request.Method in the handler and do different things accordingly. But is there a way to do such HTTP method based matching?

HTTP status code will always be set to 404

I have the following test project:

func main() {
    router := mux.NewRouter()
    router.HandleFunc("/", someHandler).Methods("GET")

    http.Handle("/", router)
    http.ListenAndServeTLS(":9000", "cert.pem", "key.pem", nil)
}

func someHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "test")
}

I then use curl https://localhost:9000 -k -I to interact with the web server. The result is the following:

HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=utf-8
Date: Sat, 14 Jun 2014 13:32:30 GMT
Content-Length: 19

The body of the request, however, is set to test. If I attempt to override the HTTP status code with w.WriteHeader(200), it does not work either. This also happens if I use a regular ListenAndServe() call. Here's my go version: go1.2.2 darwin/amd64. I use the latest master of gorilla/mux.

It would appear that HTTP status code handling is completely broken.

Add examples of applications

Requested on IRC, we don't have any fully-fledged examples of using mux, which would probably be handy to answer some commonly-asked questions.

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"

Can't create "root" route on a subrouter

r := mux.NewRouter()
sr := r.PathPrefix("/foo")
sr.Path("/").HandlerFunc(...)

// GET /foo/ -> OK
// GET /foo -> 404 Not Found

I can't find a way to get the second request to work. I thought StrictSlash might help, but it doesn't. Any suggestions?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.