gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/mux/doc.go (about)

     1  // Copyright 2012 The Gorilla Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  /*
     6  Package mux implements a request router and dispatcher.
     7  
     8  The name mux stands for "HTTP request multiplexer". Like the standard
     9  http.ServeMux, mux.Router matches incoming requests against a list of
    10  registered routes and calls a handler for the route that matches the URL
    11  or other conditions. The main features are:
    12  
    13  	* Requests can be matched based on URL host, path, path prefix, schemes,
    14  	  header and query values, HTTP methods or using custom matchers.
    15  	* URL hosts, paths and query values can have variables with an optional
    16  	  regular expression.
    17  	* Registered URLs can be built, or "reversed", which helps maintaining
    18  	  references to resources.
    19  	* Routes can be used as subrouters: nested routes are only tested if the
    20  	  parent route matches. This is useful to define groups of routes that
    21  	  share common conditions like a host, a path prefix or other repeated
    22  	  attributes. As a bonus, this optimizes request matching.
    23  	* It implements the http.Handler interface so it is compatible with the
    24  	  standard http.ServeMux.
    25  
    26  Let's start registering a couple of URL paths and handlers:
    27  
    28  	func main() {
    29  		r := mux.NewRouter()
    30  		r.HandleFunc("/", HomeHandler)
    31  		r.HandleFunc("/products", ProductsHandler)
    32  		r.HandleFunc("/articles", ArticlesHandler)
    33  		http.Handle("/", r)
    34  	}
    35  
    36  Here we register three routes mapping URL paths to handlers. This is
    37  equivalent to how http.HandleFunc() works: if an incoming request URL matches
    38  one of the paths, the corresponding handler is called passing
    39  (http.ResponseWriter, *http.Request) as parameters.
    40  
    41  Paths can have variables. They are defined using the format {name} or
    42  {name:pattern}. If a regular expression pattern is not defined, the matched
    43  variable will be anything until the next slash. For example:
    44  
    45  	r := mux.NewRouter()
    46  	r.HandleFunc("/products/{key}", ProductHandler)
    47  	r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
    48  	r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
    49  
    50  Groups can be used inside patterns, as long as they are non-capturing (?:re). For example:
    51  
    52  	r.HandleFunc("/articles/{category}/{sort:(?:asc|desc|new)}", ArticlesCategoryHandler)
    53  
    54  The names are used to create a map of route variables which can be retrieved
    55  calling mux.Vars():
    56  
    57  	vars := mux.Vars(request)
    58  	category := vars["category"]
    59  
    60  Note that if any capturing groups are present, mux will panic() during parsing. To prevent
    61  this, convert any capturing groups to non-capturing, e.g. change "/{sort:(asc|desc)}" to
    62  "/{sort:(?:asc|desc)}". This is a change from prior versions which behaved unpredictably
    63  when capturing groups were present.
    64  
    65  And this is all you need to know about the basic usage. More advanced options
    66  are explained below.
    67  
    68  Routes can also be restricted to a domain or subdomain. Just define a host
    69  pattern to be matched. They can also have variables:
    70  
    71  	r := mux.NewRouter()
    72  	// Only matches if domain is "www.example.com".
    73  	r.Host("www.example.com")
    74  	// Matches a dynamic subdomain.
    75  	r.Host("{subdomain:[a-z]+}.domain.com")
    76  
    77  There are several other matchers that can be added. To match path prefixes:
    78  
    79  	r.PathPrefix("/products/")
    80  
    81  ...or HTTP methods:
    82  
    83  	r.Methods("GET", "POST")
    84  
    85  ...or URL schemes:
    86  
    87  	r.Schemes("https")
    88  
    89  ...or header values:
    90  
    91  	r.Headers("X-Requested-With", "XMLHttpRequest")
    92  
    93  ...or query values:
    94  
    95  	r.Queries("key", "value")
    96  
    97  ...or to use a custom matcher function:
    98  
    99  	r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
   100  		return r.ProtoMajor == 0
   101  	})
   102  
   103  ...and finally, it is possible to combine several matchers in a single route:
   104  
   105  	r.HandleFunc("/products", ProductsHandler).
   106  	  Host("www.example.com").
   107  	  Methods("GET").
   108  	  Schemes("http")
   109  
   110  Setting the same matching conditions again and again can be boring, so we have
   111  a way to group several routes that share the same requirements.
   112  We call it "subrouting".
   113  
   114  For example, let's say we have several URLs that should only match when the
   115  host is "www.example.com". Create a route for that host and get a "subrouter"
   116  from it:
   117  
   118  	r := mux.NewRouter()
   119  	s := r.Host("www.example.com").Subrouter()
   120  
   121  Then register routes in the subrouter:
   122  
   123  	s.HandleFunc("/products/", ProductsHandler)
   124  	s.HandleFunc("/products/{key}", ProductHandler)
   125  	s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
   126  
   127  The three URL paths we registered above will only be tested if the domain is
   128  "www.example.com", because the subrouter is tested first. This is not
   129  only convenient, but also optimizes request matching. You can create
   130  subrouters combining any attribute matchers accepted by a route.
   131  
   132  Subrouters can be used to create domain or path "namespaces": you define
   133  subrouters in a central place and then parts of the app can register its
   134  paths relatively to a given subrouter.
   135  
   136  There's one more thing about subroutes. When a subrouter has a path prefix,
   137  the inner routes use it as base for their paths:
   138  
   139  	r := mux.NewRouter()
   140  	s := r.PathPrefix("/products").Subrouter()
   141  	// "/products/"
   142  	s.HandleFunc("/", ProductsHandler)
   143  	// "/products/{key}/"
   144  	s.HandleFunc("/{key}/", ProductHandler)
   145  	// "/products/{key}/details"
   146  	s.HandleFunc("/{key}/details", ProductDetailsHandler)
   147  
   148  Note that the path provided to PathPrefix() represents a "wildcard": calling
   149  PathPrefix("/static/").Handler(...) means that the handler will be passed any
   150  request that matches "/static/*". This makes it easy to serve static files with mux:
   151  
   152  	func main() {
   153  		var dir string
   154  
   155  		flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
   156  		flag.Parse()
   157  		r := mux.NewRouter()
   158  
   159  		// This will serve files under http://localhost:8000/static/<filename>
   160  		r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
   161  
   162  		srv := &http.Server{
   163  			Handler:      r,
   164  			Addr:         "127.0.0.1:8000",
   165  			// Good practice: enforce timeouts for servers you create!
   166  			WriteTimeout: 15 * time.Second,
   167  			ReadTimeout:  15 * time.Second,
   168  		}
   169  
   170  		log.Fatal(srv.ListenAndServe())
   171  	}
   172  
   173  Now let's see how to build registered URLs.
   174  
   175  Routes can be named. All routes that define a name can have their URLs built,
   176  or "reversed". We define a name calling Name() on a route. For example:
   177  
   178  	r := mux.NewRouter()
   179  	r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
   180  	  Name("article")
   181  
   182  To build a URL, get the route and call the URL() method, passing a sequence of
   183  key/value pairs for the route variables. For the previous route, we would do:
   184  
   185  	url, err := r.Get("article").URL("category", "technology", "id", "42")
   186  
   187  ...and the result will be a url.URL with the following path:
   188  
   189  	"/articles/technology/42"
   190  
   191  This also works for host and query value variables:
   192  
   193  	r := mux.NewRouter()
   194  	r.Host("{subdomain}.domain.com").
   195  	  Path("/articles/{category}/{id:[0-9]+}").
   196  	  Queries("filter", "{filter}").
   197  	  HandlerFunc(ArticleHandler).
   198  	  Name("article")
   199  
   200  	// url.String() will be "http://news.domain.com/articles/technology/42?filter=gorilla"
   201  	url, err := r.Get("article").URL("subdomain", "news",
   202  	                                 "category", "technology",
   203  	                                 "id", "42",
   204  	                                 "filter", "gorilla")
   205  
   206  All variables defined in the route are required, and their values must
   207  conform to the corresponding patterns. These requirements guarantee that a
   208  generated URL will always match a registered route -- the only exception is
   209  for explicitly defined "build-only" routes which never match.
   210  
   211  Regex support also exists for matching Headers within a route. For example, we could do:
   212  
   213  	r.HeadersRegexp("Content-Type", "application/(text|json)")
   214  
   215  ...and the route will match both requests with a Content-Type of `application/json` as well as
   216  `application/text`
   217  
   218  There's also a way to build only the URL host or path for a route:
   219  use the methods URLHost() or URLPath() instead. For the previous route,
   220  we would do:
   221  
   222  	// "http://news.domain.com/"
   223  	host, err := r.Get("article").URLHost("subdomain", "news")
   224  
   225  	// "/articles/technology/42"
   226  	path, err := r.Get("article").URLPath("category", "technology", "id", "42")
   227  
   228  And if you use subrouters, host and path defined separately can be built
   229  as well:
   230  
   231  	r := mux.NewRouter()
   232  	s := r.Host("{subdomain}.domain.com").Subrouter()
   233  	s.Path("/articles/{category}/{id:[0-9]+}").
   234  	  HandlerFunc(ArticleHandler).
   235  	  Name("article")
   236  
   237  	// "http://news.domain.com/articles/technology/42"
   238  	url, err := r.Get("article").URL("subdomain", "news",
   239  	                                 "category", "technology",
   240  	                                 "id", "42")
   241  
   242  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.
   243  
   244  	type MiddlewareFunc func(http.Handler) http.Handler
   245  
   246  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 (closures can access variables from the context where they are created).
   247  
   248  A very basic middleware which logs the URI of the request being handled could be written as:
   249  
   250  	func simpleMw(next http.Handler) http.Handler {
   251  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   252  			// Do stuff here
   253  			log.Println(r.RequestURI)
   254  			// Call the next handler, which can be another middleware in the chain, or the final handler.
   255  			next.ServeHTTP(w, r)
   256  		})
   257  	}
   258  
   259  Middlewares can be added to a router using `Router.Use()`:
   260  
   261  	r := mux.NewRouter()
   262  	r.HandleFunc("/", handler)
   263  	r.Use(simpleMw)
   264  
   265  A more complex authentication middleware, which maps session token to users, could be written as:
   266  
   267  	// Define our struct
   268  	type authenticationMiddleware struct {
   269  		tokenUsers map[string]string
   270  	}
   271  
   272  	// Initialize it somewhere
   273  	func (amw *authenticationMiddleware) Populate() {
   274  		amw.tokenUsers["00000000"] = "user0"
   275  		amw.tokenUsers["aaaaaaaa"] = "userA"
   276  		amw.tokenUsers["05f717e5"] = "randomUser"
   277  		amw.tokenUsers["deadbeef"] = "user0"
   278  	}
   279  
   280  	// Middleware function, which will be called for each request
   281  	func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler {
   282  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   283  			token := r.Header.Get("X-Session-Token")
   284  
   285  			if user, found := amw.tokenUsers[token]; found {
   286  				// We found the token in our map
   287  				log.Printf("Authenticated user %s\n", user)
   288  				next.ServeHTTP(w, r)
   289  			} else {
   290  				http.Error(w, "Forbidden", http.StatusForbidden)
   291  			}
   292  		})
   293  	}
   294  
   295  	r := mux.NewRouter()
   296  	r.HandleFunc("/", handler)
   297  
   298  	amw := authenticationMiddleware{tokenUsers: make(map[string]string)}
   299  	amw.Populate()
   300  
   301  	r.Use(amw.Middleware)
   302  
   303  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.
   304  
   305  */
   306  package mux