This is a creation in Article, where the information may have evolved or changed.
The Go language handles HTTP requests mainly related to two things:servemux and Handler.
First,Servemux and Handler
Servrmux is essentially an HTTP request router (or multiplexer, multiplexor) that compares the received request to a list of predefined URL paths and then invokes the associated processor (Handler) when matching to a path.
The Handler(processor) is responsible for outputting the header and body of the HTTP response, and any HTTP is satisfied. The objects of the handler interface are available as a single processor. In layman's words, an object can be signed with the Servehttp method as follows:
ServeHTTP(http.ResponseWriter, *http.Request)
The Go language's HTTP package comes with several functions that are used as common processors, such as Fileserver,notfoundhandler and Redirecthandler. Let's start with a simple, concrete example:
//File: main.gopackage mainimport ( "log" "net/http")func main() { mux := http.NewServeMux() rh := http.RedirectHandler("http://example.org", 307) mux.Handle("/foo", rh) log.Println("Listening...") http.ListenAndServe(":3000", mux)}
In the main function we only use http. Newservemux function to create an empty servemux.
Then we use http. The Redirecthandler function creates a new processor that performs a 307 redirect operation to Http://example.org for all requests received.
Next we use the servemux.handle function to register the processor with the newly created SERVEMUX, so it receives all requests on the URL path/foo to the processor.
Finally we created a new server and passed http. The Listenandserve function listens for all incoming requests by passing the Servemux that you just created to match the corresponding processor for the request.
Go ahead and run this program:
$ go run main.goListening...
Then access the Http://localhost:3000/foo in the browser and you should be able to see that the request has been redirected successfully.
Perspicacious you should be able to notice something interesting: Listenandserver's function signature is listenandserve (addr string, handler handler) , but the second argument we pass is a Servemux. The reason we can do this is because Servemux also has a servehttp method, so it's also a legitimate Handler.
For me, using Servermux as a special handler is a simplification. Instead of outputting the response itself, it passes the request to the other Handler registered to it. This may not sound a significant leap at first, but it is a very common use to have Handler chained together in Go.
Second,Defaultservemux
Defaultservemux is a Servemux, which is initialized automatically with the net/http package initialization. The Net/http package provides a set of shortcut functions (http. Handle and http. Handlefunc) to mate with Defaultservemux, which registers the processor with the Defaultservermux. When the listenandserve is not provided with other processors (that is, the second parameter is set to nil), the Defaultservemux is used internally.
Here is a defaultservemux example:
package mainimport ( "log" "net/http" "time")func timeHandler(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(time.RFC1123) w.Write([]byte("The time is: " + tm))}func main() { // Note that we skip creating the ServeMux... th := http.HandlerFunc(timeHandler) // We use http.Handle instead of mux.Handle... http.Handle("/time", th) log.Println("Listening...") // And pass nil as the handler to ListenAndServe. http.ListenAndServe(":3000", nil)}
In this example, Defaultservemuxis used, and the declaration Servemux is not displayed . in the code we have an object (in this case a timerhandler function, but it can also be a string, a struct, or anything), and we implement a servehttp (HTTP) within this function . Responsewriter, *http. Request) signature method, which is all we need to create a processor.
Any have func (http. Responsewriter, *http. Request) can be converted to a handlerfunc type. This is useful because the Handlerfunc object has a built-in Servehttp method that intelligently and conveniently invokes the content of the function we originally provided.
If you sound confused, try to look at [related source code] and you'll see that it's very simple and elegant to have a function object satisfy the Handler interface.
Iii. Explicit use of Servemux
In fact, registering to Servemux after converting a function into Handlerfunc is a common usage. When using Servemux explicitly , the Go language has a more convenient servermux.handlerfunc function.
We use a convenient way to rewrite the main () function to look like this:
func main() { mux := http.NewServeMux() mux.HandleFunc("/time", timeHandler) log.Println("Listening...") http.ListenAndServe(":3000", mux)}
In most cases, this function works well in the same way as the processor. But when things start to get more complicated, there are some limitations.
You may have noticed that, unlike the previous approach, we had to hardcode the time format into the Timehandler method. What if we want to pass some information or variables to the processor from the main () function?
An elegant way is to put our processor in a closure and bring the variables we want to use:
//File: main.gopackage mainimport ( "log" "net/http" "time")func timeHandler(format string) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write([]byte("The time is: " + tm)) } return http.HandlerFunc(fn)}func main() { mux := http.NewServeMux() th := timeHandler(time.RFC1123) mux.Handle("/time", th) log.Println("Listening...") http.ListenAndServe(":3000", mux)}
The Timehandler function now has a more subtle identity. In addition to encapsulating a function as Handler (as we did before), we now use it to return a processor. This mechanism has two key points:
(1) The first is to create an FN, an anonymous function that encapsulates the format variable into a closure. The nature of closures allows the processor to access the format variable in the local scope in any case.
(2) Next our closure function satisfies the func (HTTP. Responsewriter, *http. Request) signature. If you remember what we said before, this means that we can convert it to a handlerfunc type (satisfies the Http.handler interface). Our Timehandler function then returns after the converted Handlerfunc.
In the example above we can already pass a simple string to the processor. But in real-world applications, you can use this method to pass database connections, template groups, or other application-level contexts. Using global variables is also a good choice, and the additional benefit is to write more elegant self-contained processors for testing.
You may have seen the same wording, like this:
func timeHandler(format string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write([]byte("The time is: " + tm)) })}
Or, when returning, use an implicit conversion to the Handlerfunc type:
func timeHandler(format string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write([]byte("The time is: " + tm)) }}
Iv. Custom Handler
Let's create a custom processor that will output the current local time in a specific format:
//File: main.gopackage mainimport ( "log" "net/http" "time")type timeHandler struct { format string}func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(th.format) w.Write([]byte("The time is: " + tm))}func main() { mux := http.NewServeMux() th := &timeHandler{format: time.RFC1123} mux.Handle("/time", th) log.Println("Listening...") http.ListenAndServe(":3000", mux)}
In the main function, we initialize the Timehandler as a regular struct, and get its address with the & symbol. Then, as in the previous example, we use a mux. The Handle function to register it with the Servermux.
Now when we run this application, Servermux will hand over any requests for/time directly to the Timehandler.servehttp method.
Visit this address to see the effect: Http://localhost:3000/time
Note that we can easily reuse Timehandler in multiple routes:
func main() { mux := http.NewServeMux() th1123 := &timeHandler{format: time.RFC1123} mux.Handle("/time/rfc1123", th1123) th3339 := &timeHandler{format: time.RFC3339} mux.Handle("/time/rfc3339", th3339) log.Println("Listening...") http.ListenAndServe(":3000", mux)}