[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-4793":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":22,"hasPages":22,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":31,"readmeContent":32,"aiSummary":33,"trendingCount":16,"starSnapshotCount":16,"syncStatus":34,"lastSyncTime":35,"discoverSource":36},4793,"mux","gorilla\u002Fmux","gorilla","Package gorilla\u002Fmux is a powerful HTTP router and URL matcher for building Go web servers with 🦍","https:\u002F\u002Fgorilla.github.io",null,"Go",21831,1884,298,23,0,10,20,3,76.83,"BSD 3-Clause \"New\" or \"Revised\" License",false,"main",[25,26,7,27,28,29,5,30],"go","golang","gorilla-web-toolkit","http","middleware","router","2026-06-12 04:00:22","# gorilla\u002Fmux\n\n![testing](https:\u002F\u002Fgithub.com\u002Fgorilla\u002Fmux\u002Factions\u002Fworkflows\u002Ftest.yml\u002Fbadge.svg)\n[![codecov](https:\u002F\u002Fcodecov.io\u002Fgithub\u002Fgorilla\u002Fmux\u002Fbranch\u002Fmain\u002Fgraph\u002Fbadge.svg)](https:\u002F\u002Fcodecov.io\u002Fgithub\u002Fgorilla\u002Fmux)\n[![godoc](https:\u002F\u002Fgodoc.org\u002Fgithub.com\u002Fgorilla\u002Fmux?status.svg)](https:\u002F\u002Fgodoc.org\u002Fgithub.com\u002Fgorilla\u002Fmux)\n[![sourcegraph](https:\u002F\u002Fsourcegraph.com\u002Fgithub.com\u002Fgorilla\u002Fmux\u002F-\u002Fbadge.svg)](https:\u002F\u002Fsourcegraph.com\u002Fgithub.com\u002Fgorilla\u002Fmux?badge)\n\n\n![Gorilla Logo](https:\u002F\u002Fgithub.com\u002Fgorilla\u002F.github\u002Fassets\u002F53367916\u002Fd92caabf-98e0-473e-bfbf-ab554ba435e5)\n\nPackage `gorilla\u002Fmux` implements a request router and dispatcher for matching incoming requests to\ntheir respective handler.\n\nThe 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:\n\n* It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`.\n* Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers.\n* URL hosts, paths and query values can have variables with an optional regular expression.\n* Registered URLs can be built, or \"reversed\", which helps maintaining references to resources.\n* 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.\n\n---\n\n* [Install](#install)\n* [Examples](#examples)\n* [Matching Routes](#matching-routes)\n* [Static Files](#static-files)\n* [Serving Single Page Applications](#serving-single-page-applications) (e.g. React, Vue, Ember.js, etc.)\n* [Registered URLs](#registered-urls)\n* [Walking Routes](#walking-routes)\n* [Graceful Shutdown](#graceful-shutdown)\n* [Middleware](#middleware)\n* [Handling CORS Requests](#handling-cors-requests)\n* [Testing Handlers](#testing-handlers)\n* [Full Example](#full-example)\n\n---\n\n## Install\n\nWith a [correctly configured](https:\u002F\u002Fgolang.org\u002Fdoc\u002Finstall#testing) Go toolchain:\n\n```sh\ngo get -u github.com\u002Fgorilla\u002Fmux\n```\n\n## Examples\n\nLet's start registering a couple of URL paths and handlers:\n\n```go\nfunc main() {\n    r := mux.NewRouter()\n    r.HandleFunc(\"\u002F\", HomeHandler)\n    r.HandleFunc(\"\u002Fproducts\", ProductsHandler)\n    r.HandleFunc(\"\u002Farticles\", ArticlesHandler)\n    http.Handle(\"\u002F\", r)\n}\n```\n\nHere 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.\n\nPaths 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:\n\n```go\nr := mux.NewRouter()\nr.HandleFunc(\"\u002Fproducts\u002F{key}\", ProductHandler)\nr.HandleFunc(\"\u002Farticles\u002F{category}\u002F\", ArticlesCategoryHandler)\nr.HandleFunc(\"\u002Farticles\u002F{category}\u002F{id:[0-9]+}\", ArticleHandler)\n```\n\nThe names are used to create a map of route variables which can be retrieved calling `mux.Vars()`:\n\n```go\nfunc ArticlesCategoryHandler(w http.ResponseWriter, r *http.Request) {\n    vars := mux.Vars(r)\n    w.WriteHeader(http.StatusOK)\n    fmt.Fprintf(w, \"Category: %v\\n\", vars[\"category\"])\n}\n```\n\nAnd this is all you need to know about the basic usage. More advanced options are explained below.\n\n### Matching Routes\n\nRoutes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables:\n\n```go\nr := mux.NewRouter()\n\u002F\u002F Only matches if domain is \"www.example.com\".\nr.Host(\"www.example.com\")\n\u002F\u002F Matches a dynamic subdomain.\nr.Host(\"{subdomain:[a-z]+}.example.com\")\n```\n\nThere are several other matchers that can be added. To match path prefixes:\n\n```go\nr.PathPrefix(\"\u002Fproducts\u002F\")\n```\n\n...or HTTP methods:\n\n```go\nr.Methods(\"GET\", \"POST\")\n```\n\n...or URL schemes:\n\n```go\nr.Schemes(\"https\")\n```\n\n...or header values:\n\n```go\nr.Headers(\"X-Requested-With\", \"XMLHttpRequest\")\n```\n\n...or query values:\n\n```go\nr.Queries(\"key\", \"value\")\n```\n\n...or to use a custom matcher function:\n\n```go\nr.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {\n    return r.ProtoMajor == 0\n})\n```\n\n...and finally, it is possible to combine several matchers in a single route:\n\n```go\nr.HandleFunc(\"\u002Fproducts\", ProductsHandler).\n  Host(\"www.example.com\").\n  Methods(\"GET\").\n  Schemes(\"http\")\n```\n\nRoutes are tested in the order they were added to the router. If two routes match, the first one wins:\n\n```go\nr := mux.NewRouter()\nr.HandleFunc(\"\u002Fspecific\", specificHandler)\nr.PathPrefix(\"\u002F\").Handler(catchAllHandler)\n```\n\nSetting 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\".\n\nFor 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:\n\n```go\nr := mux.NewRouter()\ns := r.Host(\"www.example.com\").Subrouter()\n```\n\nThen register routes in the subrouter:\n\n```go\ns.HandleFunc(\"\u002Fproducts\u002F\", ProductsHandler)\ns.HandleFunc(\"\u002Fproducts\u002F{key}\", ProductHandler)\ns.HandleFunc(\"\u002Farticles\u002F{category}\u002F{id:[0-9]+}\", ArticleHandler)\n```\n\nThe 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.\n\nSubrouters 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.\n\nThere's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths:\n\n```go\nr := mux.NewRouter()\ns := r.PathPrefix(\"\u002Fproducts\").Subrouter()\n\u002F\u002F \"\u002Fproducts\u002F\"\ns.HandleFunc(\"\u002F\", ProductsHandler)\n\u002F\u002F \"\u002Fproducts\u002F{key}\u002F\"\ns.HandleFunc(\"\u002F{key}\u002F\", ProductHandler)\n\u002F\u002F \"\u002Fproducts\u002F{key}\u002Fdetails\"\ns.HandleFunc(\"\u002F{key}\u002Fdetails\", ProductDetailsHandler)\n```\n\n\n### Static Files\n\nNote that the path provided to `PathPrefix()` represents a \"wildcard\": calling\n`PathPrefix(\"\u002Fstatic\u002F\").Handler(...)` means that the handler will be passed any\nrequest that matches \"\u002Fstatic\u002F\\*\". This makes it easy to serve static files with mux:\n\n```go\nfunc main() {\n    var dir string\n\n    flag.StringVar(&dir, \"dir\", \".\", \"the directory to serve files from. Defaults to the current dir\")\n    flag.Parse()\n    r := mux.NewRouter()\n\n    \u002F\u002F This will serve files under http:\u002F\u002Flocalhost:8000\u002Fstatic\u002F\u003Cfilename>\n    r.PathPrefix(\"\u002Fstatic\u002F\").Handler(http.StripPrefix(\"\u002Fstatic\u002F\", http.FileServer(http.Dir(dir))))\n\n    srv := &http.Server{\n        Handler:      r,\n        Addr:         \"127.0.0.1:8000\",\n        \u002F\u002F Good practice: enforce timeouts for servers you create!\n        WriteTimeout: 15 * time.Second,\n        ReadTimeout:  15 * time.Second,\n    }\n\n    log.Fatal(srv.ListenAndServe())\n}\n```\n\n### Serving Single Page Applications\n\nMost of the time it makes sense to serve your SPA on a separate web server from your API,\nbut sometimes it's desirable to serve them both from one place. It's possible to write a simple\nhandler for serving your SPA (for use with React Router's [BrowserRouter](https:\u002F\u002Freacttraining.com\u002Freact-router\u002Fweb\u002Fapi\u002FBrowserRouter) for example), and leverage\nmux's powerful routing for your API endpoints.\n\n```go\npackage main\n\nimport (\n\t\"encoding\u002Fjson\"\n\t\"log\"\n\t\"net\u002Fhttp\"\n\t\"os\"\n\t\"path\u002Ffilepath\"\n\t\"time\"\n\n\t\"github.com\u002Fgorilla\u002Fmux\"\n)\n\n\u002F\u002F spaHandler implements the http.Handler interface, so we can use it\n\u002F\u002F to respond to HTTP requests. The path to the static directory and\n\u002F\u002F path to the index file within that static directory are used to\n\u002F\u002F serve the SPA in the given static directory.\ntype spaHandler struct {\n\tstaticPath string\n\tindexPath  string\n}\n\n\u002F\u002F ServeHTTP inspects the URL path to locate a file within the static dir\n\u002F\u002F on the SPA handler. If a file is found, it will be served. If not, the\n\u002F\u002F file located at the index path on the SPA handler will be served. This\n\u002F\u002F is suitable behavior for serving an SPA (single page application).\nfunc (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\t\u002F\u002F Join internally call path.Clean to prevent directory traversal\n\tpath := filepath.Join(h.staticPath, r.URL.Path)\n\n\t\u002F\u002F check whether a file exists or is a directory at the given path\n\tfi, err := os.Stat(path)\n\tif os.IsNotExist(err) || fi.IsDir() {\n\t\t\u002F\u002F file does not exist or path is a directory, serve index.html\n\t\thttp.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))\n\t\treturn\n\t}\n\n\tif err != nil {\n\t\t\u002F\u002F if we got an error (that wasn't that the file doesn't exist) stating the\n\t\t\u002F\u002F file, return a 500 internal server error and stop\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n        return\n\t}\n\n\t\u002F\u002F otherwise, use http.FileServer to serve the static file\n\thttp.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r)\n}\n\nfunc main() {\n\trouter := mux.NewRouter()\n\n\trouter.HandleFunc(\"\u002Fapi\u002Fhealth\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\u002F\u002F an example API handler\n\t\tjson.NewEncoder(w).Encode(map[string]bool{\"ok\": true})\n\t})\n\n\tspa := spaHandler{staticPath: \"build\", indexPath: \"index.html\"}\n\trouter.PathPrefix(\"\u002F\").Handler(spa)\n\n\tsrv := &http.Server{\n\t\tHandler: router,\n\t\tAddr:    \"127.0.0.1:8000\",\n\t\t\u002F\u002F Good practice: enforce timeouts for servers you create!\n\t\tWriteTimeout: 15 * time.Second,\n\t\tReadTimeout:  15 * time.Second,\n\t}\n\n\tlog.Fatal(srv.ListenAndServe())\n}\n```\n\n### Registered URLs\n\nNow let's see how to build registered URLs.\n\nRoutes 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:\n\n```go\nr := mux.NewRouter()\nr.HandleFunc(\"\u002Farticles\u002F{category}\u002F{id:[0-9]+}\", ArticleHandler).\n  Name(\"article\")\n```\n\nTo build a URL, get the route and call the `URL()` method, passing a sequence of key\u002Fvalue pairs for the route variables. For the previous route, we would do:\n\n```go\nurl, err := r.Get(\"article\").URL(\"category\", \"technology\", \"id\", \"42\")\n```\n\n...and the result will be a `url.URL` with the following path:\n\n```\n\"\u002Farticles\u002Ftechnology\u002F42\"\n```\n\nThis also works for host and query value variables:\n\n```go\nr := mux.NewRouter()\nr.Host(\"{subdomain}.example.com\").\n  Path(\"\u002Farticles\u002F{category}\u002F{id:[0-9]+}\").\n  Queries(\"filter\", \"{filter}\").\n  HandlerFunc(ArticleHandler).\n  Name(\"article\")\n\n\u002F\u002F url.String() will be \"http:\u002F\u002Fnews.example.com\u002Farticles\u002Ftechnology\u002F42?filter=gorilla\"\nurl, err := r.Get(\"article\").URL(\"subdomain\", \"news\",\n                                 \"category\", \"technology\",\n                                 \"id\", \"42\",\n                                 \"filter\", \"gorilla\")\n```\n\nAll 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.\n\nRegex support also exists for matching Headers within a route. For example, we could do:\n\n```go\nr.HeadersRegexp(\"Content-Type\", \"application\u002F(text|json)\")\n```\n\n...and the route will match both requests with a Content-Type of `application\u002Fjson` as well as `application\u002Ftext`\n\nThere'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:\n\n```go\n\u002F\u002F \"http:\u002F\u002Fnews.example.com\u002F\"\nhost, err := r.Get(\"article\").URLHost(\"subdomain\", \"news\")\n\n\u002F\u002F \"\u002Farticles\u002Ftechnology\u002F42\"\npath, err := r.Get(\"article\").URLPath(\"category\", \"technology\", \"id\", \"42\")\n```\n\nAnd if you use subrouters, host and path defined separately can be built as well:\n\n```go\nr := mux.NewRouter()\ns := r.Host(\"{subdomain}.example.com\").Subrouter()\ns.Path(\"\u002Farticles\u002F{category}\u002F{id:[0-9]+}\").\n  HandlerFunc(ArticleHandler).\n  Name(\"article\")\n\n\u002F\u002F \"http:\u002F\u002Fnews.example.com\u002Farticles\u002Ftechnology\u002F42\"\nurl, err := r.Get(\"article\").URL(\"subdomain\", \"news\",\n                                 \"category\", \"technology\",\n                                 \"id\", \"42\")\n```\n\nTo find all the required variables for a given route when calling `URL()`, the method `GetVarNames()` is available:\n```go\nr := mux.NewRouter()\nr.Host(\"{domain}\").\n    Path(\"\u002F{group}\u002F{item_id}\").\n    Queries(\"some_data1\", \"{some_data1}\").\n    Queries(\"some_data2\", \"{some_data2}\").\n    Name(\"article\")\n\n\u002F\u002F Will print [domain group item_id some_data1 some_data2] \u003Cnil>\nfmt.Println(r.Get(\"article\").GetVarNames())\n\n```\n### Walking Routes\n\nThe `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example,\nthe following prints all of the registered routes:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net\u002Fhttp\"\n\t\"strings\"\n\n\t\"github.com\u002Fgorilla\u002Fmux\"\n)\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n\treturn\n}\n\nfunc main() {\n\tr := mux.NewRouter()\n\tr.HandleFunc(\"\u002F\", handler)\n\tr.HandleFunc(\"\u002Fproducts\", handler).Methods(\"POST\")\n\tr.HandleFunc(\"\u002Farticles\", handler).Methods(\"GET\")\n\tr.HandleFunc(\"\u002Farticles\u002F{id}\", handler).Methods(\"GET\", \"PUT\")\n\tr.HandleFunc(\"\u002Fauthors\", handler).Queries(\"surname\", \"{surname}\")\n\terr := r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {\n\t\tpathTemplate, err := route.GetPathTemplate()\n\t\tif err == nil {\n\t\t\tfmt.Println(\"ROUTE:\", pathTemplate)\n\t\t}\n\t\tpathRegexp, err := route.GetPathRegexp()\n\t\tif err == nil {\n\t\t\tfmt.Println(\"Path regexp:\", pathRegexp)\n\t\t}\n\t\tqueriesTemplates, err := route.GetQueriesTemplates()\n\t\tif err == nil {\n\t\t\tfmt.Println(\"Queries templates:\", strings.Join(queriesTemplates, \",\"))\n\t\t}\n\t\tqueriesRegexps, err := route.GetQueriesRegexp()\n\t\tif err == nil {\n\t\t\tfmt.Println(\"Queries regexps:\", strings.Join(queriesRegexps, \",\"))\n\t\t}\n\t\tmethods, err := route.GetMethods()\n\t\tif err == nil {\n\t\t\tfmt.Println(\"Methods:\", strings.Join(methods, \",\"))\n\t\t}\n\t\tfmt.Println()\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\tfmt.Println(err)\n\t}\n\n\thttp.Handle(\"\u002F\", r)\n}\n```\n\n### Graceful Shutdown\n\nGo 1.8 introduced the ability to [gracefully shutdown](https:\u002F\u002Fgolang.org\u002Fdoc\u002Fgo1.8#http_shutdown) a `*http.Server`. Here's how to do that alongside `mux`:\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"flag\"\n    \"log\"\n    \"net\u002Fhttp\"\n    \"os\"\n    \"os\u002Fsignal\"\n    \"time\"\n\n    \"github.com\u002Fgorilla\u002Fmux\"\n)\n\nfunc main() {\n    var wait time.Duration\n    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\")\n    flag.Parse()\n\n    r := mux.NewRouter()\n    \u002F\u002F Add your routes as needed\n\n    srv := &http.Server{\n        Addr:         \"0.0.0.0:8080\",\n        \u002F\u002F Good practice to set timeouts to avoid Slowloris attacks.\n        WriteTimeout: time.Second * 15,\n        ReadTimeout:  time.Second * 15,\n        IdleTimeout:  time.Second * 60,\n        Handler: r, \u002F\u002F Pass our instance of gorilla\u002Fmux in.\n    }\n\n    \u002F\u002F Run our server in a goroutine so that it doesn't block.\n    go func() {\n        if err := srv.ListenAndServe(); err != nil {\n            log.Println(err)\n        }\n    }()\n\n    c := make(chan os.Signal, 1)\n    \u002F\u002F We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)\n    \u002F\u002F SIGKILL, SIGQUIT or SIGTERM (Ctrl+\u002F) will not be caught.\n    signal.Notify(c, os.Interrupt)\n\n    \u002F\u002F Block until we receive our signal.\n    \u003C-c\n\n    \u002F\u002F Create a deadline to wait for.\n    ctx, cancel := context.WithTimeout(context.Background(), wait)\n    defer cancel()\n    \u002F\u002F Doesn't block if no connections, but will otherwise wait\n    \u002F\u002F until the timeout deadline.\n    srv.Shutdown(ctx)\n    \u002F\u002F Optionally, you could run srv.Shutdown in a goroutine and block on\n    \u002F\u002F \u003C-ctx.Done() if your application should wait for other services\n    \u002F\u002F to finalize based on context cancellation.\n    log.Println(\"shutting down\")\n    os.Exit(0)\n}\n```\n\n### Middleware\n\nMux supports the addition of middlewares to a [Router](https:\u002F\u002Fgodoc.org\u002Fgithub.com\u002Fgorilla\u002Fmux#Router), which are executed in the order they are added if a match is found, including its subrouters.\nMiddlewares 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.\n\nMux middlewares are defined using the de facto standard type:\n\n```go\ntype MiddlewareFunc func(http.Handler) http.Handler\n```\n\nTypically, 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.\n\nA very basic middleware which logs the URI of the request being handled could be written as:\n\n```go\nfunc loggingMiddleware(next http.Handler) http.Handler {\n    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n        \u002F\u002F Do stuff here\n        log.Println(r.RequestURI)\n        \u002F\u002F Call the next handler, which can be another middleware in the chain, or the final handler.\n        next.ServeHTTP(w, r)\n    })\n}\n```\n\nMiddlewares can be added to a router using `Router.Use()`:\n\n```go\nr := mux.NewRouter()\nr.HandleFunc(\"\u002F\", handler)\nr.Use(loggingMiddleware)\n```\n\nA more complex authentication middleware, which maps session token to users, could be written as:\n\n```go\n\u002F\u002F Define our struct\ntype authenticationMiddleware struct {\n\ttokenUsers map[string]string\n}\n\n\u002F\u002F Initialize it somewhere\nfunc (amw *authenticationMiddleware) Populate() {\n\tamw.tokenUsers[\"00000000\"] = \"user0\"\n\tamw.tokenUsers[\"aaaaaaaa\"] = \"userA\"\n\tamw.tokenUsers[\"05f717e5\"] = \"randomUser\"\n\tamw.tokenUsers[\"deadbeef\"] = \"user0\"\n}\n\n\u002F\u002F Middleware function, which will be called for each request\nfunc (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler {\n    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n        token := r.Header.Get(\"X-Session-Token\")\n\n        if user, found := amw.tokenUsers[token]; found {\n        \t\u002F\u002F We found the token in our map\n        \tlog.Printf(\"Authenticated user %s\\n\", user)\n        \t\u002F\u002F Pass down the request to the next middleware (or final handler)\n        \tnext.ServeHTTP(w, r)\n        } else {\n        \t\u002F\u002F Write an error and stop the handler chain\n        \thttp.Error(w, \"Forbidden\", http.StatusForbidden)\n        }\n    })\n}\n```\n\n```go\nr := mux.NewRouter()\nr.HandleFunc(\"\u002F\", handler)\n\namw := authenticationMiddleware{tokenUsers: make(map[string]string)}\namw.Populate()\n\nr.Use(amw.Middleware)\n```\n\nNote: 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.\n\n### Handling CORS Requests\n\n[CORSMethodMiddleware](https:\u002F\u002Fgodoc.org\u002Fgithub.com\u002Fgorilla\u002Fmux#CORSMethodMiddleware) intends to make it easier to strictly set the `Access-Control-Allow-Methods` response header.\n\n* You will still need to use your own CORS handler to set the other CORS headers such as `Access-Control-Allow-Origin`\n* 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\n* If you do not specify any methods, then:\n> _Important_: there must be an `OPTIONS` method matcher for the middleware to set the headers.\n\nHere is an example of using `CORSMethodMiddleware` along with a custom `OPTIONS` handler to set all the required CORS headers:\n\n```go\npackage main\n\nimport (\n\t\"net\u002Fhttp\"\n\t\"github.com\u002Fgorilla\u002Fmux\"\n)\n\nfunc main() {\n    r := mux.NewRouter()\n\n    \u002F\u002F IMPORTANT: you must specify an OPTIONS method matcher for the middleware to set CORS headers\n    r.HandleFunc(\"\u002Ffoo\", fooHandler).Methods(http.MethodGet, http.MethodPut, http.MethodPatch, http.MethodOptions)\n    r.Use(mux.CORSMethodMiddleware(r))\n    \n    http.ListenAndServe(\":8080\", r)\n}\n\nfunc fooHandler(w http.ResponseWriter, r *http.Request) {\n    w.Header().Set(\"Access-Control-Allow-Origin\", \"*\")\n    if r.Method == http.MethodOptions {\n        return\n    }\n\n    w.Write([]byte(\"foo\"))\n}\n```\n\nAnd an request to `\u002Ffoo` using something like:\n\n```bash\ncurl localhost:8080\u002Ffoo -v\n```\n\nWould look like:\n\n```bash\n*   Trying ::1...\n* TCP_NODELAY set\n* Connected to localhost (::1) port 8080 (#0)\n> GET \u002Ffoo HTTP\u002F1.1\n> Host: localhost:8080\n> User-Agent: curl\u002F7.59.0\n> Accept: *\u002F*\n> \n\u003C HTTP\u002F1.1 200 OK\n\u003C Access-Control-Allow-Methods: GET,PUT,PATCH,OPTIONS\n\u003C Access-Control-Allow-Origin: *\n\u003C Date: Fri, 28 Jun 2019 20:13:30 GMT\n\u003C Content-Length: 3\n\u003C Content-Type: text\u002Fplain; charset=utf-8\n\u003C \n* Connection #0 to host localhost left intact\nfoo\n```\n\n### Testing Handlers\n\nTesting 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_.\n\nFirst, our simple HTTP handler:\n\n```go\n\u002F\u002F endpoints.go\npackage main\n\nfunc HealthCheckHandler(w http.ResponseWriter, r *http.Request) {\n    \u002F\u002F A very simple health check.\n    w.Header().Set(\"Content-Type\", \"application\u002Fjson\")\n    w.WriteHeader(http.StatusOK)\n\n    \u002F\u002F In the future we could report back on the status of our DB, or our cache\n    \u002F\u002F (e.g. Redis) by performing a simple PING, and include them in the response.\n    io.WriteString(w, `{\"alive\": true}`)\n}\n\nfunc main() {\n    r := mux.NewRouter()\n    r.HandleFunc(\"\u002Fhealth\", HealthCheckHandler)\n\n    log.Fatal(http.ListenAndServe(\"localhost:8080\", r))\n}\n```\n\nOur test code:\n\n```go\n\u002F\u002F endpoints_test.go\npackage main\n\nimport (\n    \"net\u002Fhttp\"\n    \"net\u002Fhttp\u002Fhttptest\"\n    \"testing\"\n)\n\nfunc TestHealthCheckHandler(t *testing.T) {\n    \u002F\u002F Create a request to pass to our handler. We don't have any query parameters for now, so we'll\n    \u002F\u002F pass 'nil' as the third parameter.\n    req, err := http.NewRequest(\"GET\", \"\u002Fhealth\", nil)\n    if err != nil {\n        t.Fatal(err)\n    }\n\n    \u002F\u002F We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.\n    rr := httptest.NewRecorder()\n    handler := http.HandlerFunc(HealthCheckHandler)\n\n    \u002F\u002F Our handlers satisfy http.Handler, so we can call their ServeHTTP method\n    \u002F\u002F directly and pass in our Request and ResponseRecorder.\n    handler.ServeHTTP(rr, req)\n\n    \u002F\u002F Check the status code is what we expect.\n    if status := rr.Code; status != http.StatusOK {\n        t.Errorf(\"handler returned wrong status code: got %v want %v\",\n            status, http.StatusOK)\n    }\n\n    \u002F\u002F Check the response body is what we expect.\n    expected := `{\"alive\": true}`\n    if rr.Body.String() != expected {\n        t.Errorf(\"handler returned unexpected body: got %v want %v\",\n            rr.Body.String(), expected)\n    }\n}\n```\n\nIn the case that our routes have [variables](#examples), we can pass those in the request. We could write\n[table-driven tests](https:\u002F\u002Fdave.cheney.net\u002F2013\u002F06\u002F09\u002Fwriting-table-driven-tests-in-go) to test multiple\npossible route variables as needed.\n\n```go\n\u002F\u002F endpoints.go\nfunc main() {\n    r := mux.NewRouter()\n    \u002F\u002F A route with a route variable:\n    r.HandleFunc(\"\u002Fmetrics\u002F{type}\", MetricsHandler)\n\n    log.Fatal(http.ListenAndServe(\"localhost:8080\", r))\n}\n```\n\nOur test file, with a table-driven test of `routeVariables`:\n\n```go\n\u002F\u002F endpoints_test.go\nfunc TestMetricsHandler(t *testing.T) {\n    tt := []struct{\n        routeVariable string\n        shouldPass bool\n    }{\n        {\"goroutines\", true},\n        {\"heap\", true},\n        {\"counters\", true},\n        {\"queries\", true},\n        {\"adhadaeqm3k\", false},\n    }\n\n    for _, tc := range tt {\n        path := fmt.Sprintf(\"\u002Fmetrics\u002F%s\", tc.routeVariable)\n        req, err := http.NewRequest(\"GET\", path, nil)\n        if err != nil {\n            t.Fatal(err)\n        }\n\n        rr := httptest.NewRecorder()\n\t\n\t\u002F\u002F To add the vars to the context, \n\t\u002F\u002F we need to create a router through which we can pass the request.\n\trouter := mux.NewRouter()\n        router.HandleFunc(\"\u002Fmetrics\u002F{type}\", MetricsHandler)\n        router.ServeHTTP(rr, req)\n\n        \u002F\u002F In this case, our MetricsHandler returns a non-200 response\n        \u002F\u002F for a route variable it doesn't know about.\n        if rr.Code == http.StatusOK && !tc.shouldPass {\n            t.Errorf(\"handler should have failed on routeVariable %s: got %v want %v\",\n                tc.routeVariable, rr.Code, http.StatusOK)\n        }\n    }\n}\n```\n\n## Full Example\n\nHere's a complete, runnable example of a small `mux` based server:\n\n```go\npackage main\n\nimport (\n    \"net\u002Fhttp\"\n    \"log\"\n    \"github.com\u002Fgorilla\u002Fmux\"\n)\n\nfunc YourHandler(w http.ResponseWriter, r *http.Request) {\n    w.Write([]byte(\"Gorilla!\\n\"))\n}\n\nfunc main() {\n    r := mux.NewRouter()\n    \u002F\u002F Routes consist of a path and a handler function.\n    r.HandleFunc(\"\u002F\", YourHandler)\n\n    \u002F\u002F Bind to a port and pass our router in\n    log.Fatal(http.ListenAndServe(\":8000\", r))\n}\n```\n\n## License\n\nBSD licensed. See the LICENSE file for details.\n","gorilla\u002Fmux 是一个用于构建 Go 语言 Web 服务器的强大 HTTP 路由器和 URL 匹配器。它支持基于 URL 主机、路径、路径前缀、协议、头部和查询值以及 HTTP 方法等多种条件进行请求匹配，并允许在 URL 中使用变量和正则表达式。此外，gorilla\u002Fmux 支持注册 URL 的构建与反转，有助于维护资源引用；还提供了子路由功能，使得共享公共条件的路由组可以更高效地处理请求。此项目非常适合需要高度自定义路由逻辑的 Web 应用开发场景，如 RESTful API 构建、单页面应用（SPA）服务等。",2,"2026-06-11 03:00:30","top_language"]