Before there is an article more superficial analysis of the Golang server how to achieve, there is Handler
, DefaultServeMux
HandlerFunc
the use of.
We now know that the DefaultServeMux
pattern
place to store and what handler
we call it 路由
, then we may think, since we golang
can achieve this 路由
, can we also imitate one?
First we need a container () that can hold the client's request 路由
.
Create a routing structure body
type CopyRouter struct { router map[string]map[string]http.HandlerFunc}
Here we create a DefaultServeMux
route like that.
Client requests are stored in a route
func (c *CopyRouter) HandleFunc(method, pattern string, handle http.HandlerFunc) { if method == "" { panic("Method can not be null!") } if pattern == "" { panic("Pattern can not be null!") } if _, ok := c.router[method][pattern]; ok { panic("Pattern Exists!") } if c.router == nil { c.router = make(map[string]map[string]http.HandlerFunc) } if c.router[method] == nil { c.router[method] = make(map[string]http.HandlerFunc) } c.router[method][pattern] = handle}
Here we imitate the source of ServeMux
each of the URL
corresponding to handler
save.
Implementing the Handler interface
func (c *CopyRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) { if f, ok := c.router[r.Method][r.URL.String()]; ok { f.ServeHTTP(w, r) }}
Here's why we want to implement this handler interface, because we find that in the Listenandserve method, the final call h.ServeHTTP(w, r)
, then we just need to let our definition of the route implementation Handler
interface can be.
Get a route
func NewRouter() *CopyRouter { return new(CopyRouter)}
Here, our own definition of the route is complete, let's look at how to use it.
func sayHi(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w,"Hi")}func main() { copyRouter := copyrouter.NewRouter() copyRouter.HandleFunc("GET","/sayHi", sayHi) log.Fatal(http.ListenAndServe("localhost:8080", copyRouter))}
This completes a high-imitation version of the custom route, is not and Golang provide us with a ServeMux
very similar, of course, we this route is a low version, there are many details are not processed.
Now look again, our main function inside the code is not very beautiful, every time to write get or post method, then we can provide a more beautiful way? Yes, then let's wrap it up again.
func (c *CopyRouter) GET(pattern string, handler http.HandlerFunc){ c.HandleFunc("GET", pattern, handler)}func (c *CopyRouter) POST(pattern string, handler http.HandlerFunc){ c.HandleFunc("POST", pattern, handler)}...
Then modify the calling method.
copyRouter.GET("/sayHi",sayHi)
Does it look a lot more beautiful now? Yes, a lot of the web
framework is the same, why it feels smooth to use, because these gods are standing in the perspective of our developers to consider the problem, provides a very convenient use of some, encapsulation is very perfect.
Think about what else we can do with this custom route, and if we want to record every access request, what should we do? Also very simple, we just need to write the logic in the ServeHTTP
method, we can modify our code a little bit.
func (c *CopyRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) { if f, ok := c.router[r.Method][r.URL.String()]; ok { func (handler http.Handler){ start := time.Now() log.Printf(" 请求 [%s] 开始时间为 : %v\n", r.URL.String(), start) f.ServeHTTP(w, r) log.Printf(" 请求 [%s] 完成时间为 : %v\n", r.URL.String(), time.Since(start)) }(f) }}
Here we have added a function to record the request time, so there are more things to do in this custom route.
Also, is it possible to change this type to handler when we define this routing structure? That map[string]map[string]http.HandlerFunc
is, it is possible to modify this type map[string]map[string]http.Handler
, but we need to make a change in the main method when we call it.
copyRouter.GET("/sayHi",HandlerFunc(sayHi))
Make a cast here, but this is not very beautiful.
See here, we should focus on a type in the source code, that is HandlerFunc
.
type HandlerFunc func(ResponseWriter, *Request)func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r)}
Here HandlerFunc
is the role of an adapter, this is a very clever design, I have to say that the golang
interface is really very sophisticated design.