github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/gorilla/mux/README.md (about) 1 # gorilla/mux 2 3 [](https://godoc.org/github.com/gorilla/mux) 4 [](https://travis-ci.org/gorilla/mux) 5 [](https://sourcegraph.com/github.com/gorilla/mux?badge) 6 7  8 9 https://www.gorillatoolkit.org/pkg/mux 10 11 Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to 12 their respective handler. 13 14 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: 15 16 * It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`. 17 * Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers. 18 * URL hosts, paths and query values can have variables with an optional regular expression. 19 * Registered URLs can be built, or "reversed", which helps maintaining references to resources. 20 * 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. 21 22 --- 23 24 * [Install](#install) 25 * [Examples](#examples) 26 * [Matching Routes](#matching-routes) 27 * [Static Files](#static-files) 28 * [Registered URLs](#registered-urls) 29 * [Walking Routes](#walking-routes) 30 * [Graceful Shutdown](#graceful-shutdown) 31 * [Middleware](#middleware) 32 * [Testing Handlers](#testing-handlers) 33 * [Full Example](#full-example) 34 35 --- 36 37 ## Install 38 39 With a [correctly configured](https://golang.org/doc/install#testing) Go toolchain: 40 41 ```sh 42 go get -u github.com/gorilla/mux 43 ``` 44 45 ## Examples 46 47 Let's start registering a couple of URL paths and handlers: 48 49 ```go 50 func main() { 51 r := mux.NewRouter() 52 r.HandleFunc("/", HomeHandler) 53 r.HandleFunc("/products", ProductsHandler) 54 r.HandleFunc("/articles", ArticlesHandler) 55 http.Handle("/", r) 56 } 57 ``` 58 59 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. 60 61 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: 62 63 ```go 64 r := mux.NewRouter() 65 r.HandleFunc("/products/{key}", ProductHandler) 66 r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) 67 r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) 68 ``` 69 70 The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`: 71 72 ```go 73 func ArticlesCategoryHandler(w http.ResponseWriter, r *http.Request) { 74 vars := mux.Vars(r) 75 w.WriteHeader(http.StatusOK) 76 fmt.Fprintf(w, "Category: %v\n", vars["category"]) 77 } 78 ``` 79 80 And this is all you need to know about the basic usage. More advanced options are explained below. 81 82 ### Matching Routes 83 84 Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables: 85 86 ```go 87 r := mux.NewRouter() 88 // Only matches if domain is "www.example.com". 89 r.Host("www.example.com") 90 // Matches a dynamic subdomain. 91 r.Host("{subdomain:[a-z]+}.example.com") 92 ``` 93 94 There are several other matchers that can be added. To match path prefixes: 95 96 ```go 97 r.PathPrefix("/products/") 98 ``` 99 100 ...or HTTP methods: 101 102 ```go 103 r.Methods("GET", "POST") 104 ``` 105 106 ...or URL schemes: 107 108 ```go 109 r.Schemes("https") 110 ``` 111 112 ...or header values: 113 114 ```go 115 r.Headers("X-Requested-With", "XMLHttpRequest") 116 ``` 117 118 ...or query values: 119 120 ```go 121 r.Queries("key", "value") 122 ``` 123 124 ...or to use a custom matcher function: 125 126 ```go 127 r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { 128 return r.ProtoMajor == 0 129 }) 130 ``` 131 132 ...and finally, it is possible to combine several matchers in a single route: 133 134 ```go 135 r.HandleFunc("/products", ProductsHandler). 136 Host("www.example.com"). 137 Methods("GET"). 138 Schemes("http") 139 ``` 140 141 Routes are tested in the order they were added to the router. If two routes match, the first one wins: 142 143 ```go 144 r := mux.NewRouter() 145 r.HandleFunc("/specific", specificHandler) 146 r.PathPrefix("/").Handler(catchAllHandler) 147 ``` 148 149 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". 150 151 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: 152 153 ```go 154 r := mux.NewRouter() 155 s := r.Host("www.example.com").Subrouter() 156 ``` 157 158 Then register routes in the subrouter: 159 160 ```go 161 s.HandleFunc("/products/", ProductsHandler) 162 s.HandleFunc("/products/{key}", ProductHandler) 163 s.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) 164 ``` 165 166 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. 167 168 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. 169 170 There's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths: 171 172 ```go 173 r := mux.NewRouter() 174 s := r.PathPrefix("/products").Subrouter() 175 // "/products/" 176 s.HandleFunc("/", ProductsHandler) 177 // "/products/{key}/" 178 s.HandleFunc("/{key}/", ProductHandler) 179 // "/products/{key}/details" 180 s.HandleFunc("/{key}/details", ProductDetailsHandler) 181 ``` 182 183 184 ### Static Files 185 186 Note that the path provided to `PathPrefix()` represents a "wildcard": calling 187 `PathPrefix("/static/").Handler(...)` means that the handler will be passed any 188 request that matches "/static/\*". This makes it easy to serve static files with mux: 189 190 ```go 191 func main() { 192 var dir string 193 194 flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir") 195 flag.Parse() 196 r := mux.NewRouter() 197 198 // This will serve files under http://localhost:8000/static/<filename> 199 r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir)))) 200 201 srv := &http.Server{ 202 Handler: r, 203 Addr: "127.0.0.1:8000", 204 // Good practice: enforce timeouts for servers you create! 205 WriteTimeout: 15 * time.Second, 206 ReadTimeout: 15 * time.Second, 207 } 208 209 log.Fatal(srv.ListenAndServe()) 210 } 211 ``` 212 213 ### Registered URLs 214 215 Now let's see how to build registered URLs. 216 217 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: 218 219 ```go 220 r := mux.NewRouter() 221 r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). 222 Name("article") 223 ``` 224 225 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: 226 227 ```go 228 url, err := r.Get("article").URL("category", "technology", "id", "42") 229 ``` 230 231 ...and the result will be a `url.URL` with the following path: 232 233 ``` 234 "/articles/technology/42" 235 ``` 236 237 This also works for host and query value variables: 238 239 ```go 240 r := mux.NewRouter() 241 r.Host("{subdomain}.example.com"). 242 Path("/articles/{category}/{id:[0-9]+}"). 243 Queries("filter", "{filter}"). 244 HandlerFunc(ArticleHandler). 245 Name("article") 246 247 // url.String() will be "http://news.example.com/articles/technology/42?filter=gorilla" 248 url, err := r.Get("article").URL("subdomain", "news", 249 "category", "technology", 250 "id", "42", 251 "filter", "gorilla") 252 ``` 253 254 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. 255 256 Regex support also exists for matching Headers within a route. For example, we could do: 257 258 ```go 259 r.HeadersRegexp("Content-Type", "application/(text|json)") 260 ``` 261 262 ...and the route will match both requests with a Content-Type of `application/json` as well as `application/text` 263 264 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: 265 266 ```go 267 // "http://news.example.com/" 268 host, err := r.Get("article").URLHost("subdomain", "news") 269 270 // "/articles/technology/42" 271 path, err := r.Get("article").URLPath("category", "technology", "id", "42") 272 ``` 273 274 And if you use subrouters, host and path defined separately can be built as well: 275 276 ```go 277 r := mux.NewRouter() 278 s := r.Host("{subdomain}.example.com").Subrouter() 279 s.Path("/articles/{category}/{id:[0-9]+}"). 280 HandlerFunc(ArticleHandler). 281 Name("article") 282 283 // "http://news.example.com/articles/technology/42" 284 url, err := r.Get("article").URL("subdomain", "news", 285 "category", "technology", 286 "id", "42") 287 ``` 288 289 ### Walking Routes 290 291 The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example, 292 the following prints all of the registered routes: 293 294 ```go 295 package main 296 297 import ( 298 "fmt" 299 "github.com/hellobchain/newcryptosm/http" 300 "strings" 301 302 "github.com/hellobchain/third_party/gorilla/mux" 303 ) 304 305 func handler(w http.ResponseWriter, r *http.Request) { 306 return 307 } 308 309 func main() { 310 r := mux.NewRouter() 311 r.HandleFunc("/", handler) 312 r.HandleFunc("/products", handler).Methods("POST") 313 r.HandleFunc("/articles", handler).Methods("GET") 314 r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT") 315 r.HandleFunc("/authors", handler).Queries("surname", "{surname}") 316 err := r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { 317 pathTemplate, err := route.GetPathTemplate() 318 if err == nil { 319 fmt.Println("ROUTE:", pathTemplate) 320 } 321 pathRegexp, err := route.GetPathRegexp() 322 if err == nil { 323 fmt.Println("Path regexp:", pathRegexp) 324 } 325 queriesTemplates, err := route.GetQueriesTemplates() 326 if err == nil { 327 fmt.Println("Queries templates:", strings.Join(queriesTemplates, ",")) 328 } 329 queriesRegexps, err := route.GetQueriesRegexp() 330 if err == nil { 331 fmt.Println("Queries regexps:", strings.Join(queriesRegexps, ",")) 332 } 333 methods, err := route.GetMethods() 334 if err == nil { 335 fmt.Println("Methods:", strings.Join(methods, ",")) 336 } 337 fmt.Println() 338 return nil 339 }) 340 341 if err != nil { 342 fmt.Println(err) 343 } 344 345 http.Handle("/", r) 346 } 347 ``` 348 349 ### Graceful Shutdown 350 351 Go 1.8 introduced the ability to [gracefully shutdown](https://golang.org/doc/go1.8#http_shutdown) a `*http.Server`. Here's how to do that alongside `mux`: 352 353 ```go 354 package main 355 356 import ( 357 "context" 358 "flag" 359 "log" 360 "github.com/hellobchain/newcryptosm/http" 361 "os" 362 "os/signal" 363 "time" 364 365 "github.com/hellobchain/third_party/gorilla/mux" 366 ) 367 368 func main() { 369 var wait time.Duration 370 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") 371 flag.Parse() 372 373 r := mux.NewRouter() 374 // Add your routes as needed 375 376 srv := &http.Server{ 377 Addr: "0.0.0.0:8080", 378 // Good practice to set timeouts to avoid Slowloris attacks. 379 WriteTimeout: time.Second * 15, 380 ReadTimeout: time.Second * 15, 381 IdleTimeout: time.Second * 60, 382 Handler: r, // Pass our instance of gorilla/mux in. 383 } 384 385 // Run our server in a goroutine so that it doesn't block. 386 go func() { 387 if err := srv.ListenAndServe(); err != nil { 388 log.Println(err) 389 } 390 }() 391 392 c := make(chan os.Signal, 1) 393 // We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C) 394 // SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught. 395 signal.Notify(c, os.Interrupt) 396 397 // Block until we receive our signal. 398 <-c 399 400 // Create a deadline to wait for. 401 ctx, cancel := context.WithTimeout(context.Background(), wait) 402 defer cancel() 403 // Doesn't block if no connections, but will otherwise wait 404 // until the timeout deadline. 405 srv.Shutdown(ctx) 406 // Optionally, you could run srv.Shutdown in a goroutine and block on 407 // <-ctx.Done() if your application should wait for other services 408 // to finalize based on context cancellation. 409 log.Println("shutting down") 410 os.Exit(0) 411 } 412 ``` 413 414 ### Middleware 415 416 Mux supports the addition of middlewares to a [Router](https://godoc.org/github.com/gorilla/mux#Router), which are executed in the order they are added if a match is found, including its subrouters. 417 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. 418 419 Mux middlewares are defined using the de facto standard type: 420 421 ```go 422 type MiddlewareFunc func(http.Handler) http.Handler 423 ``` 424 425 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. 426 427 A very basic middleware which logs the URI of the request being handled could be written as: 428 429 ```go 430 func loggingMiddleware(next http.Handler) http.Handler { 431 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 432 // Do stuff here 433 log.Println(r.RequestURI) 434 // Call the next handler, which can be another middleware in the chain, or the final handler. 435 next.ServeHTTP(w, r) 436 }) 437 } 438 ``` 439 440 Middlewares can be added to a router using `Router.Use()`: 441 442 ```go 443 r := mux.NewRouter() 444 r.HandleFunc("/", handler) 445 r.Use(loggingMiddleware) 446 ``` 447 448 A more complex authentication middleware, which maps session token to users, could be written as: 449 450 ```go 451 // Define our struct 452 type authenticationMiddleware struct { 453 tokenUsers map[string]string 454 } 455 456 // Initialize it somewhere 457 func (amw *authenticationMiddleware) Populate() { 458 amw.tokenUsers["00000000"] = "user0" 459 amw.tokenUsers["aaaaaaaa"] = "userA" 460 amw.tokenUsers["05f717e5"] = "randomUser" 461 amw.tokenUsers["deadbeef"] = "user0" 462 } 463 464 // Middleware function, which will be called for each request 465 func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler { 466 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 467 token := r.Header.Get("X-Session-Token") 468 469 if user, found := amw.tokenUsers[token]; found { 470 // We found the token in our map 471 log.Printf("Authenticated user %s\n", user) 472 // Pass down the request to the next middleware (or final handler) 473 next.ServeHTTP(w, r) 474 } else { 475 // Write an error and stop the handler chain 476 http.Error(w, "Forbidden", http.StatusForbidden) 477 } 478 }) 479 } 480 ``` 481 482 ```go 483 r := mux.NewRouter() 484 r.HandleFunc("/", handler) 485 486 amw := authenticationMiddleware{} 487 amw.Populate() 488 489 r.Use(amw.Middleware) 490 ``` 491 492 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. 493 494 ### Testing Handlers 495 496 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_. 497 498 First, our simple HTTP handler: 499 500 ```go 501 // endpoints.go 502 package main 503 504 func HealthCheckHandler(w http.ResponseWriter, r *http.Request) { 505 // A very simple health check. 506 w.WriteHeader(http.StatusOK) 507 w.Header().Set("Content-Type", "application/json") 508 509 // In the future we could report back on the status of our DB, or our cache 510 // (e.g. Redis) by performing a simple PING, and include them in the response. 511 io.WriteString(w, `{"alive": true}`) 512 } 513 514 func main() { 515 r := mux.NewRouter() 516 r.HandleFunc("/health", HealthCheckHandler) 517 518 log.Fatal(http.ListenAndServe("localhost:8080", r)) 519 } 520 ``` 521 522 Our test code: 523 524 ```go 525 // endpoints_test.go 526 package main 527 528 import ( 529 "github.com/hellobchain/newcryptosm/http" 530 "net/http/httptest" 531 "testing" 532 ) 533 534 func TestHealthCheckHandler(t *testing.T) { 535 // Create a request to pass to our handler. We don't have any query parameters for now, so we'll 536 // pass 'nil' as the third parameter. 537 req, err := http.NewRequest("GET", "/health", nil) 538 if err != nil { 539 t.Fatal(err) 540 } 541 542 // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response. 543 rr := httptest.NewRecorder() 544 handler := http.HandlerFunc(HealthCheckHandler) 545 546 // Our handlers satisfy http.Handler, so we can call their ServeHTTP method 547 // directly and pass in our Request and ResponseRecorder. 548 handler.ServeHTTP(rr, req) 549 550 // Check the status code is what we expect. 551 if status := rr.Code; status != http.StatusOK { 552 t.Errorf("handler returned wrong status code: got %v want %v", 553 status, http.StatusOK) 554 } 555 556 // Check the response body is what we expect. 557 expected := `{"alive": true}` 558 if rr.Body.String() != expected { 559 t.Errorf("handler returned unexpected body: got %v want %v", 560 rr.Body.String(), expected) 561 } 562 } 563 ``` 564 565 In the case that our routes have [variables](#examples), we can pass those in the request. We could write 566 [table-driven tests](https://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go) to test multiple 567 possible route variables as needed. 568 569 ```go 570 // endpoints.go 571 func main() { 572 r := mux.NewRouter() 573 // A route with a route variable: 574 r.HandleFunc("/metrics/{type}", MetricsHandler) 575 576 log.Fatal(http.ListenAndServe("localhost:8080", r)) 577 } 578 ``` 579 580 Our test file, with a table-driven test of `routeVariables`: 581 582 ```go 583 // endpoints_test.go 584 func TestMetricsHandler(t *testing.T) { 585 tt := []struct{ 586 routeVariable string 587 shouldPass bool 588 }{ 589 {"goroutines", true}, 590 {"heap", true}, 591 {"counters", true}, 592 {"queries", true}, 593 {"adhadaeqm3k", false}, 594 } 595 596 for _, tc := range tt { 597 path := fmt.Sprintf("/metrics/%s", tc.routeVariable) 598 req, err := http.NewRequest("GET", path, nil) 599 if err != nil { 600 t.Fatal(err) 601 } 602 603 rr := httptest.NewRecorder() 604 605 // Need to create a router that we can pass the request through so that the vars will be added to the context 606 router := mux.NewRouter() 607 router.HandleFunc("/metrics/{type}", MetricsHandler) 608 router.ServeHTTP(rr, req) 609 610 // In this case, our MetricsHandler returns a non-200 response 611 // for a route variable it doesn't know about. 612 if rr.Code == http.StatusOK && !tc.shouldPass { 613 t.Errorf("handler should have failed on routeVariable %s: got %v want %v", 614 tc.routeVariable, rr.Code, http.StatusOK) 615 } 616 } 617 } 618 ``` 619 620 ## Full Example 621 622 Here's a complete, runnable example of a small `mux` based server: 623 624 ```go 625 package main 626 627 import ( 628 "github.com/hellobchain/newcryptosm/http" 629 "log" 630 "github.com/hellobchain/third_party/gorilla/mux" 631 ) 632 633 func YourHandler(w http.ResponseWriter, r *http.Request) { 634 w.Write([]byte("Gorilla!\n")) 635 } 636 637 func main() { 638 r := mux.NewRouter() 639 // Routes consist of a path and a handler function. 640 r.HandleFunc("/", YourHandler) 641 642 // Bind to a port and pass our router in 643 log.Fatal(http.ListenAndServe(":8000", r)) 644 } 645 ``` 646 647 ## License 648 649 BSD licensed. See the LICENSE file for details.