Go Language Net/http Package usage mode

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

The content of this article is very basic and very easy to understand. The original address, the feeling is the most clear of the net/http use of the package to tell a piece, so the translation of sharing.

Fundamentals of Everything: Servemux and Handler

The Go language handles HTTP requests mainly related to two things: ServeMux and Handler .

ServrMuxis essentially an HTTP request router (or called a multiplexer, multiplexor). It compares the received request to a list of pre-defined URL paths and then invokes the associated processor (Handler) when matching to the path.

The processor (Handler) is responsible for outputting the header and body of the HTTP response. Any http.Handler object that satisfies the interface can be used as a processor. In layman's words, an object can only be signed with the following ServeHTTP method:

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:

$ mkdir handler-example$ cd handler-example$ touch main.go
//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)}

Quickly over the code:

    • In the main function we only use the http.NewServeMux function to create an empty one ServeMux .

    • Then we use the http.RedirectHandler function to create a new processor that will perform 307 redirect operations to all requests received http://example.org .

    • Next we use the function to register the processor with the ServeMux.Handle newly created ServeMux one, so it /foo receives all requests on the URL path to the processor.

    • Finally, we create a new server and http.ListenAndServe listen to all incoming requests through the function, passing the created to match the ServeMux corresponding processor for the request.

Go ahead and run this program:

$ go run main.goListening...

Then access in the browser http://localhost:3000/foo , you should be able to find that the request has been successfully redirected.

Perspicacious you should be able to notice something interesting: ListenAndServer the function signature is ListenAndServe(addr string, handler Handler) , but the second argument we pass is a ServeMux .

We can do this because ServeMux there is a ServeHTTP way, so it is also legal Handler .

For me, to be ServerMux used 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 does not sound a significant leap at first-but it Handler is a very common use to link together in Go.

Custom Processor (handlers)

Let's create a custom processor that will output the current local time in a specific format:

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))}

The code itself is not the focus in this example.

The real point is that we have an object (in this case timerHandler , a struct, but it can also be a string, a function, or anything), and we implement a signature method on this object ServeHTTP(http.ResponseWriter, *http.Request) , which is all we need to create a processor.

Let's integrate this into a specific example:

//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)}

mainfunction, we initialize it like a regular struct, and timeHandler & get its address with a symbol. Then, as in the previous example, we use a mux.Handle function to register it with ServerMux .

Now when we run this application, we will ServerMux /time give any request directly to the timeHandler.ServeHTTP method processing.

Visit this address to see the effect: Http://localhost:3000/time.

Note that we can easily reuse multiple routes timeHandler :

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)}

To use a function as a processor

For simple cases (such as the example above), it is a little cumbersome to define a new ServerHTTP custom type with a method. Let's take a look at another way, we use the http.HandlerFunc type to let a regular function satisfy Handler the condition as an interface.

Any func(http.ResponseWriter, *http.Request) function that has a signature can be converted to a HandlerFunc type. This is useful because the HandlerFunc object has built-in ServeHTTP methods that can intelligently and conveniently invoke the content of the function we originally provided.

If you're still a little confused, try looking [related source code]http://golang.org/src/pkg/net .... You will see that Handler it is very simple and elegant to have a function object satisfy the interface.

Let's use this technique to re-implement the timeHandler application:

//File: main.gopackage 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() {  mux := http.NewServeMux()  // Convert the timeHandler function to a HandlerFunc type  th := http.HandlerFunc(timeHandler)  // And add it to the ServeMux  mux.Handle("/time", th)  log.Println("Listening...")  http.ListenAndServe(":3000", mux)}

In fact, converting a function into HandlerFunc post-registration ServeMux is a very common use, so the Go language provides a convenient way to do this: ServerMux.HandlerFunc method.

We use the convenient way main() to rewrite the 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 timeHandler the method. What if we want to main() pass some information or variables to the processor from the 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)}

timeHandlerThe function now has a more ingenious 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:

The first is to create one fn , which is an anonymous function that format encapsulates variables into a closure. The nature of closures allows the processor to access variables at the local scope in any case format .

Next our closure function satisfies the func(http.ResponseWriter, *http.Request) signature. If you remember what we said before, it means that we can convert it to a HandlerFunc type (which satisfies the http.Handler interface). Our timeHandler function then returns after the conversion 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 HandlerFunc implicit conversion to a 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))  }}

More Convenient Defaultservemux

You may have seen it in many places DefaultServeMux , from the simplest Hello World example to the source of the go language.

It took me a long time to realize that DefaultServerMux there was nothing special in the place. That's DefaultServerMux what we used before ServerMux , but it net/httpp was automatically initialized as the package was initialized. The related lines in the Go source code are as follows:

var DefaultServeMux = NewServeMux()

net/httpThe package provides a set of shortcuts to match DefaultServeMux : http.Handle and http.HandleFunc . The only difference between these functions and the functions of similar names that we've seen before is that they register the processor with the DefaultServerMux one we created before ServeMux .

In addition, ListenAndServe if no other processor is provided (that is, the second parameter is set nil ), it is used internally DefaultServeMux .

So, as a last step, we used DefaultServeMux to rewrite our timeHandler app:

//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() {  // Note that we skip creating the ServeMux...  var format string = time.RFC1123  th := timeHandler(format)  // 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)}
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.