httprouter

repository·master·Indexed 12 days ago

https://github.com/julienschmidt/httprouter

A high-performance, lightweight HTTP request router for Go that uses a radix tree for efficient matching. It supports dynamic named parameters (:name), catch-all routes (*name), and provides a 3-argument Handle API. The router implements the http.Handler interface, allowing compatibility with standard net/http middleware and handlers.

Tokens
3.9K
Snippets
15
Records
19
Agent score
97%

What's inside httprouter

  1. Use Named Parameters in routing patterns

    master

    Named parameters use the :name syntax in the routing pattern. They match exactly one path segment. You can retrieve the value using the ByName(name string) method on the httprouter.Params object.

    Example behavior for pattern /user/:user:

    • /user/gordon -> match
    • /user/you -> match
    • /user/gordon/profile -> no match
    • /user/ -> no match
  2. Use middleware with HttpRouter

    master
    HttpRouter implements the http.Handler interface, which means you can wrap the router with any standard Go middleware compatible with net/http. You can chain middleware before the router to handle cross-cutting concerns like logging, recovery, or authentication. Alternatively, you can use third-party middleware libraries like Gorilla handlers.
  3. Use Catch-All parameters in routing patterns

    master

    Catch-all parameters use the *name syntax. They match everything from that point forward in the path and must always be at the end of the pattern.

    Example behavior for pattern /src/*filepath:

    • /src/ -> match
    • /src/somefile.go -> match
    • /src/subdir/somefile.go -> match
  4. Implement multi-domain or sub-domain routing

    master

    To serve different content based on the request host (domain or sub-domain), you can create a custom http.Handler that acts as a switch. This handler maps hostnames to specific http.Handler instances (such as different httprouter.Router instances).

    type HostSwitch map[string]http.Handler
    
    func (hs HostSwitch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	if handler := hs[r.Host]; handler != nil {
    		handler.ServeHTTP(w, r)
    	} else {
    		http.Error(w, "Forbidden", 403)
    	}
    }
    
    func main() {
    	router := httprouter.New()
    	router.GET("/", Index)
    
    	hs := make(HostSwitch)
    	hs["example.com:12345"] = router
    
    	http.ListenAndServe(":12345", hs)
    }
  5. Chain requests using the NotFound handler

    master

    You can use the Router.NotFound field to delegate requests that don't match any registered routes to another http.Handler. This is useful for chaining multiple routers or serving static files as a fallback.

    Note: You may need to set Router.HandleMethodNotAllowed = false to avoid conflicts when chaining handlers.

    // Serve static files from the ./public directory when no route matches
    router.NotFound = http.FileServer(http.Dir("public"))
  6. Use named and catch-all parameters in paths

    master

    HttpRouter supports two types of dynamic path segments:

    1. Named parameters (:name): Matches any character until the next / or the end of the path.

      • Path: /blog/:category/:post
      • Request: /blog/go/request-routers matches category="go" and post="request-routers".
    2. Catch-all parameters (*name): Matches everything from that point until the end of the path, including slashes. Catch-all parameters must be the final element in the path.

      • Path: /files/*filepath
      • Request: /files/templates/article.html matches filepath="/templates/article.html".
    // Named parameter example
    router.GET("/hello/:name", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
        fmt.Fprintf(w, "Hello, %s!", ps.ByName("name"))
    })
    
    // Catch-all parameter example
    router.GET("/files/*filepath", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
        fmt.Fprintf(w, "File path: %s", ps.ByName("filepath"))
    })
  7. Initialize a new Router

    master

    Use httprouter.New() to create a new router instance. By default, the router has the following features enabled:

    • RedirectTrailingSlash: Automatically redirects requests with or without a trailing slash.
    • RedirectFixedPath: Automatically redirects requests with superfluous path elements (like // or ..) or incorrect casing to the cleaned, correct path.
    • HandleMethodNotAllowed: Returns a 405 Method Not Allowed if the path exists but the HTTP method is incorrect.
    • HandleOPTIONS: Automatically responds to OPTIONS requests with the appropriate Allow header.
    router := httprouter.New()
  8. Implement Basic Authentication for routes

    master

    You can create a middleware wrapper for httprouter.Handle to enforce Basic Authentication (RFC 2617). The wrapper checks the Authorization header and only delegates to the original handler if the credentials match the required user and password.

    func BasicAuth(h httprouter.Handle, requiredUser, requiredPassword string) httprouter.Handle {
    	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    		user, password, hasAuth := r.BasicAuth()
    
    		if hasAuth && user == requiredUser && password == requiredPassword {
    			h(w, r, ps)
    		} else {
    			w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
    			http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
    		}
    	}
    }
    
    // Usage:
    // router.GET("/protected/", BasicAuth(Protected, "user", "pass"))
  9. Basic Usage of HttpRouter

    master

    HttpRouter provides a New() function to create a router instance. You can register routes for specific HTTP methods (like GET) using patterns. The most efficient way to use it is with the 3-argument Handle API, which provides httprouter.Params directly to your handler.

    Note: Because HttpRouter uses explicit matches, you cannot register a static route and a parameter route for the same path segment and method (e.g., you cannot have both /user/new and /user/:user for GET).

    package main
    
    import (
        "fmt"
        "net/http"
        "log"
    
        "github.com/julienschmidt/httprouter"
    )
    
    func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
        fmt.Fprint(w, "Welcome!\n")
    }
    
    func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
        fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
    }
    
    func main() {
        router := httprouter.New()
        router.GET("/", Index)
        router.GET("/hello/:name", Hello)
    
        log.Fatal(http.ListenAndServe(":8080", router))
    }
  10. Access parameters from standard http.Handler

    master

    If you are using standard http.Handler or http.HandlerFunc (via router.Handler or router.HandlerFunc), the named parameters are not passed as a third argument. Instead, they are stored in the request's context. You can retrieve them using httprouter.ParamsFromContext(r.Context()).

    func Hello(w http.ResponseWriter, r *http.Request) {
        params := httprouter.ParamsFromContext(r.Context())
    
        fmt.Fprintf(w, "hello, %s!\n", params.ByName("name"))
    }
  11. Configure Global OPTIONS responses and CORS

    master

    You can customize how the router responds to OPTIONS requests (useful for CORS preflight) by assigning an http.HandlerFunc to the Router.GlobalOPTIONS field.

    router.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if r.Header.Get("Access-Control-Request-Method") != "" {
            // Set CORS headers
            header := w.Header()
            header.Set("Access-Control-Allow-Methods", header.Get("Allow"))
            header.Set("Access-Control-Allow-Origin", "*")
        }
    
        // Adjust status code to 204
        w.WriteHeader(http.StatusNoContent)
    })