SoFunction
Updated on 2025-03-05

In-depth study on the use and pattern matching of Go standard library-ServeMux

introduction

This article is the second article in the series [In-depth understanding of Go Standard Library]

Article 1:Starting of http server

Article 2: The use of ServeMux and pattern matching👈

According to the description in the Golang documentation,ServeMuxis an HTTP request multiplexer (HTTP Request multiplexer). It matches the request URL and registered pattern according to certain rules and executes the most matching pattern.Handler

If you don't know what isHandler, strongly recommend that you read it first:Article 1: Starting of http server

Basic use

ImplementedHandlerinterface

type Handler interface {
 ServeHTTP(ResponseWriter, *Request)
}

Provide two functions to register different Path processing functions

  • What is received isHandlerInterface implementation

  •  Anonymous functions are received

type PathBar struct {
}

func (m PathBar) ServeHTTP(w , r *) {
 ([]byte("Receive path bar"))
 return
}

func main() {
 mx := ()

 ("/bar/", PathBar{})
 ("/foo", func(w , r *) {
  ([]byte("Receive path foo"))
 })

 (":8009", mx)
}

🌲 Implementing the interface through type conversion

It is worth mentioningThe implementation of the bottom layer is still called

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
 if handler == nil {
  panic("http: nil handler")
 }
 (pattern, HandlerFunc(handler))
}

HandlerFunc(handler)This is not a function call, but a type conversion

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
 f(w, r)
}

Byhandler func(ResponseWriter, *Request)Convert to typeHandlerFunc, and typeHandlerFuncImplementedHandlerinterface

🌲 Global default value

When not setWhen attributes,A global variable will be usedDefaultServeMux *ServeMuxCome asValue of

The following code is no different from the above

func main() {
 ("/bar/", PathBar{})
 ("/foo", func(w , r *) {
  ([]byte("Receive path foo"))
 })

 (":8009", nil)
}

Pattern Matching

Preprocessing

The preprocessed isRequested url, for the convenience of matching, no processing will be done during registration

  • Remove the port number in the host

  • For URLs include..or.ServeMux will sort out the path for requests and match them to the appropriate routing mode.

  • For duplicates included in the URL/ServeMux will redirect the request

func main() {
 mx := ()

 ("/abc/def", func(writer , request *) {
  (writer, , )
 })

 (":8009", mx)
}

🌲 The preprocessed isRequested url

Patterns will not be processed, and the requested urls are processed into standard format

So if you register the following pattern, you will not be hit anyway

func main() {
 mx := ()

 ("/abc//def", func(writer , request *) {
  (writer, , )
 })
}

Whether it is/abc/defstill/abc//defCan't be hit

$ curl 127.0.0.1:8009/abc/def
404 page not found
$ curl 127.0.0.1:8009/abc//def
<a href="/abc/def" rel="external nofollow"  rel="external nofollow"     >Moved Permanently</a>.

🌲 Bring..or.Request and duplication/The request is handled differently

Include..or.After sorting out, match it to the appropriate routing mode.It will not be redirected

$ curl  127.0.0.1:8009/ccc/../abc/./def
127.0.0.1:8009 /abc/def

With duplicate/, will redirect

$ curl -v  127.0.0.1:8009/abc//def
*   Trying 127.0.0.1:8009...
* Connected to 127.0.0.1 (127.0.0.1) port 8009 (#0)
> GET /abc//def HTTP/1.1
> Host: 127.0.0.1:8009
> User-Agent: curl/7.79.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Content-Type: text/html; charset=utf-8
< Location: /abc/def
< Date: Thu, 10 Nov 2022 16:05:13 GMT
< Content-Length: 43
< 
<a href="/abc/def" rel="external nofollow"  rel="external nofollow"     >Moved Permanently</a>.

* Connection #0 to host 127.0.0.1 left intact

Path Match

There are two ways to register a routing mode in ServeMux.Fixed root pathFor example "/", withSubtree starting with root path, for example "/images/"

🌲 Fixed paths (fixed, rooted paths)

Fixed root pathIt is to specify a fixed URL to match the request exactly

🌲 The rooted subtrees

Subtree starting with root pathIt conforms to the principle of longest path matching. For example, we have registered two subpaths./image/gif/and/image/, the URL is/image/gif/The request will first match the first routing pattern, and other paths will match/image/

⚠️ Note:

1. Any/The ending paths are all regarded as subtrees starting with the root path, so/Also regarded as a subtree starting with a root path, it not only matches/, and it will also match all requests that are not matched by other routing patterns.

func main() {
 mx := ()

 ("/", func(writer , request *) {
  (writer, ())
 })

 (":8009", mx)
}
$ curl 127.0.0.1:8009/abc
/abc

2. If only one subtree path is registered (/End) and the request URL does not/At the end, ServeMux will return a redirect. If you add another one, no/If the ending pattern will match exactly, there will be no such behavior.

For example, we only registered subpaths/abc/The server will automatically/abcRequest redirection as/abc/

func main() {
 mx := ()
 ("/abc/", func(writer , request *) {
  (writer, ())
 })
 (":8009", mx)
}
$ curl -v 127.0.0.1:8009/abc
*   Trying 127.0.0.1:8009...
* Connected to 127.0.0.1 (127.0.0.1) port 8009 (#0)
> GET /abc HTTP/1.1
> Host: 127.0.0.1:8009
> User-Agent: curl/7.79.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Content-Type: text/html; charset=utf-8
< Location: /abc/
< Date: Thu, 10 Nov 2022 15:30:13 GMT
< Content-Length: 40
< 
<a href="/abc/" rel="external nofollow"   >Moved Permanently</a>.

* Connection #0 to host 127.0.0.1 left intact

If we don't want the server to redirect automatically, we just need to add another one/abcJust the mode

func main() {
 mx := ()

 ("/abc/", func(writer , request *) {
  (writer, ())
 })

 ("/abc", func(writer , request *) {
  (writer, ())
 })

 (":8009", mx)
}
$ curl  127.0.0.1:8009/abc 
/abc

Domain name matching (Host-specific patterns)

ServeMux also supports exact matching based on the host name. When matching, the host will be strictly matched, and the path matching also follows the above principles.

⚠️ Note:

The priority of domain names will be higher, so you can register a path with domain names and a path without domain names.

func main() {
 mx := ()

 ("/abc/",
  func(writer , request *) {
   (writer, , ())
  })

 ("/abc/", func(writer , request *) {
  (writer, , ())
 })

 (":8009", mx)
}

Will match the first handler, and other domain names will match the second one

$ curl -H 'HOST:'  127.0.0.1:8009/abc/
 /abc/

$ curl -H 'HOST:'  127.0.0.1:8009/abc 
 /abc

Method and path parameter matching (method, path specific patterns)

The latest features are still being discussed, rough patterns will look like the following

/golang/go/discussions/60227 

/item/
POST /item/{user}
/item/{user}
/item/{user}/{id}
/item/{$}
POST /item/{user}

The above is the detailed content of the in-depth exploration of the use and pattern matching of Go standard library - ServeMux. For more information about Go ServeMux pattern matching, please follow my other related articles!