Multiplexer根据URL将请求路由给指定的Handler。Handler用于处理请求并给予响应。更严格地说,用来读取请求体、并将请求对应的响应字段(respones header)写入ResponseWriter中,而后返回。web
什么是Handler。它是一个接口,定义在net/http/server.go中:浏览器
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
也就是说,实现了ServerHTTP方法的都是Handler。注意ServerHTTP方法的参数:http.ResponesWriter接口和Request指针。网络
在Handler的注释中,给出了几点主要的说明:闭包
再看看ResponseWriter接口的定义:并发
// A ResponseWriter interface is used by an HTTP handler to // construct an HTTP response. // // A ResponseWriter may not be used after the Handler.ServeHTTP method // has returned. type ResponseWriter interface { Header() Header Write([]byte) (int, error) WriteHeader(statusCode int) }
注释中已经说明,ResponseWriter接口的做用是用于构造HTTP response。且明确指定了Handler.ServerHTTP方法返回之后就不能再使用ResponseWriter了。函数
这个接口有3个方法:url
type Header map[string][]string
很显然,ResponseWriter的做用是构造响应header,并将响应header和响应数据经过网络连接发送给客户端。指针
在启动go http自带的web服务时,调用了函数ListenAndServe()。这个函数的定义以下:code
func ListenAndServe(addr string, handler Handler) error
该函数有两个参数,第一个参数是自带的web监听地址和端口,第二个参数是Handler,用来处理每一个接进来的http request,但通常第二个参数设置为nil,表示调用默认的Multiplexer:DefaultServeMux。这个默认的ServeMux惟一的做用,是将请求根据URL路由给对应的handler进行处理。server
var DefaultServeMux = &defaultServeMux
这里有两个问题:
先看第二个问题,很简单,由于ServeMux类型定义了ServeHTTP()方法,它实现了Handler接口:
type ServeMux struct { // Has unexported fields. } func NewServeMux() *ServeMux func (mux *ServeMux) Handle(pattern string, handler Handler) func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)
前面说过,只要实现了ServerHTTP()方法的类型,就是一个Handler。而DefaultServeMux是默认的ServeMux,因此它是一个Handler。
关于第一个问题,看一个示例就知道了。
package main import ( "fmt" "net/http" ) // MyHandler实现Handler接口 type MyHandler struct{} func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!\n") } func main() { handler := MyHandler{} server := http.Server{ Addr: "127.0.0.1:8080", Handler: &handler, // 以&handler做为第二个参数 } server.ListenAndServe() }
上面的示例中定义了一个handler,它实现的ServeHTTP()方法只有一个做用,输出Hello World!
。而且将这个handler做为ListenAndServe()的第二个参数。
注意,上面以&handler
做为参数而非handler
,由于此处MyHandler中实现的ServerHTTP()方法的receiver是指针类型的,因此MyHandler的实例对象也必须是指针类型的,如此才能实现Handler接口。
启动这个web服务后,以不一样的URL去访问它,将老是获得彻底相同的响应结果:
很显然,当handler做为ListenAndServe()的第二个参数时,任意请求都会使用这个惟一的handler进行处理。
因此,建议将第二个参数设置为nil(或者上面的Serve Struct不指定Handler字段),它表示调用默认的DefaultServeMux做为handler,使得每一个访问请求都会调用这个特殊的handler,而这个handler的做用是将请求根据url路由给不一样的handler。
另外须要注意的是,http包中提供的Handle()和HandleFunc()函数实际上是DefaultServeMux.XXX的封装,因此直接调用http.Handle()和http.HandleFunc()其实是在调用DefaultServeMux.Handle()和DefaultServeMux.HandleFunc()。
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) } func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) }
下面是使用了DefaultServeMux的示例。
建立了两个handler,一个handler用于对应/hello
,该handler用于输出Hello
,另外一个handler用于对应world
,该handler用于输出World
:
package main import ( "fmt" "net/http" ) type HelloHandler struct{} type WorldHandler struct{} func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello\n") } func (h *WorldHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "World\n") } func main() { helloHandler := HelloHandler{} worldHandler := WorldHandler{} server := http.Server{ Addr: "127.0.0.1:8080", } http.Handle("/hello",&helloHandler) http.Handle("/world",&worldHandler) server.ListenAndServe() }
下面是访问的结果:
除了使用Handle处理http请求,也能使用HandleFunc()处理。
先看一个使用HandleFunc()处理请求的示例,示例的效果和前文是同样的。
package main import ( "fmt" "net/http" ) func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello\n") } func world(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "World\n") } func main() { server := http.Server{ Addr: "127.0.0.1:8080", } http.HandleFunc("/hello", hello) http.HandleFunc("/world", world) server.ListenAndServe() }
下面是访问的结果:
Go有一个函数HandleFunc(),它表示使用第二个参数的函数做为handler,处理匹配到的url路径请求。
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
不难看出,HandleFunc()使得咱们能够直接使用一个函数做为handler,而不须要自定义一个实现Handler接口的类型。正如上面的示例中,咱们没有定义Handler类型,也没有去实现ServeHTTP()方法,而是直接定义函数,并将其做为handler。
换句话说,HandleFunc()使得咱们能够更简便地为某些url路径注册handler。可是,使用HandleFunc()毕竟是图简便,有时候不得不使用Handle(),好比咱们肯定要定义一个type。
说实话,一开始感受挺乱的。
Handle()和HandleFunc()是函数,用来给url绑定handler。Handler和HandlerFunc类型,用来处理请求。
看Handle()、HandleFunc()以及Handler、HandlerFunc的定义就已经很清晰了:
func Handle(pattern string, handler Handler) {} func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {} type Handler interface { ServeHTTP(ResponseWriter, *Request) } type HandlerFunc func(ResponseWriter, *Request) func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)
Handle()和HandleFunc()都是为某个url路径模式绑定一个对应的handler,只不过HandleFunc()是直接使用函数做为handler,而Handle()是使用Handler类型的实例做为handler。
Handler接口的实例都实现了ServeHTTP()方法,都用来处理请求并响应给客户端。
HandlerFunc类型不是接口,但它有一个方法ServeHTTP(),也就是说HandlerFunc其实也是一种Handler。
由于HandlerFunc是类型,只要某个函数的签名是func(ResponseWriter, *Request)
,它就是HandlerFunc类型的一个实例。另外一方面,这个类型的实例(多是参数、多是返回值类型)能够和某个签名为func(ResponseWriter, *Request)
的函数进行互相赋值。这个过程可能很隐式,但确实常常出现相关的用法。
例如:
// 一个函数类型的handler func myhf(ResponseWriter, *Request){} // 以HandlerFunc类型做为参数类型 func a(hf HandlerFunc){} // 因此,能够将myhf做为a()的参数 a(myhf)
实际上,可使用HandlerFunc()进行转换。例若有一个函数world(),它的参数是合理的,使用HandlerFunc(world)表示将其转换为一个Handler。这个转换、适应在后面会常常用到。
例如:
// 两个函数 func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello\n") } func world(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "World\n") } func main() { server := http.Server{ Addr: "127.0.0.1:8080", } // 第一个使用HandleFunc()为路径注册hello()函数handler // 第二个使用Handle()为路径注册转换后的handler http.HandleFunc("/hello", hello) http.Handle("/world", http.HandlerFunc(world)) server.ListenAndServe() }
上面的示例中,Handle()函数的第二个参数要求的是Handler类型,使用http.HandlerFunc(world)
就将函数world()转换成了Handler类型的一个实例。
handler是用来处理http请求的,处理过程可能会很简单,也可能会很复杂。复杂的状况下,可能没法使用一个单独的handler来完成工做,毕竟handler只是一个函数。尽管咱们能够直接在这个函数中调用其它函数。
很常常地,可能handler中须要嵌套其它handler,甚至多层嵌套,这就是链式handler。
因为Handle()或HandleFunc()注册的时候须要指定参数类型,因此handler嵌套的时候,也要关注handler的参数类型以及返回类型。看下面示例就会明白参数类型和返回类型是怎么要求的。
代码以下:
package main import ( "fmt" "net/http" ) func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!\n") } func log(hf http.HandlerFunc) http.HandlerFunc { count := 0 return func(w http.ResponseWriter, r *http.Request) { count++ fmt.Printf("Handler Function called %d times\n", count) hf(w, r) } } func main() { server := http.Server{ Addr: "127.0.0.1:8080", } http.HandleFunc("/hello", log(hello)) server.ListenAndServe() }
屡次访问http://127.0.0.1:8080/hello
,将在浏览器中输出"Hello World!",但同时会在运行这个go程序的终端上屡次输出如下内容:
$ go run test.go Handler Function called 1 times Handler Function called 2 times Handler Function called 3 times Handler Function called 4 times Handler Function called 5 times
上面的示例中,主要看下面两段代码:
func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!\n") } func log(hf http.HandlerFunc) http.HandlerFunc { count := 0 return func(w http.ResponseWriter, r *http.Request) { count++ fmt.Printf("Handler Function called %d times\n", count) hf(w, r) } }
hello()是一个普通的HandlerFunc类型函数,由于它的签名符合HandlerFunc类型,因此它是HandlerFunc类型的一个实例。而log()函数正是以HandlerFunc类型做为参数的,因此前面的示例代码中,将hello函数做为了log函数的参数:
http.HandleFunc("/hello", log(hello))
HandleFunc()的第二个参数要求是HandlerFunc类型的,因此log()的返回值是HandlerFunc类型。在log()中,使用匿名函数做为它的返回值,这里的这个匿名函数是一个闭包(由于引用了外层函数的变量hf和count)。这个匿名函数最后调用hf(w,r)
,因为hf是HandlerFunc类型的一个实例,因此能够如此调用。
上面体现了HandlerFunc嵌套时候关于参数以及返回值的一些细节。
上面的示例中还有一个细节须要引发注意:为何每次访问时,上面的count都会记住以前的值并自增,而不是重置为0后自增。
之因此有这个疑问,多是认为每次访问时,请求处理完成后handler就退出了,闭包虽然会记住外层函数的自由变量count,但也会由于处理完成后退出,致使每次访问都重置为0后自增。但实际上,handler是注册在给定路径上的,只要web服务没有退出,这个handler就一直不会退出,不会由于每次访问都从新注册handler。因此,闭包handler一直引用着hf和count这两个自由变量。
将上面的HandlerFunc嵌套HandlerFunc修改一下,变成Handler嵌套HandlerFunc。
package main import ( "fmt" "net/http" ) type MyHandler struct{} func (wh *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!\n") } func log(h http.Handler) http.Handler { count := 0 f := func(w http.ResponseWriter, r *http.Request) { count++ fmt.Printf("Handler Function called %d times\n", count) h.ServeHTTP(w, r) } return http.HandlerFunc(f) } func main() { myHandler := MyHandler{} server := http.Server{ Addr: "127.0.0.1:8080", } http.Handle("/hello", log(&myHandler)) server.ListenAndServe() }
逻辑也很简单,无非是将HandlerFunc转换成Handler。
思考一下,Handler是否能够嵌套Handler,或者Handler嵌套HandlerFunc。能够,可是很不方便,由于ServeHTTP()方法限制了无法调用其它的Handler,除非定义的某个Handler是嵌套在某个Handler类型中的类型。