前面的第1章和第2章曾经简单地介绍过处理器以及处理器函数,现在是时候详细地谈谈它们的定义了。在Go语言中,一个处理器就是一个拥有ServeHTTP
方法的接口,这个ServeHTTP
方法需要接受两个参数:第一个参数是一个ResponseWriter
接口,而第二个参数则是一个指向Request
结构的指针。换句话说,任何接口只要拥有一个ServeHTTP
方法,并且该方法带有以下签名(signature),那么它就是一个处理器:
ServeHTTP(http.ResponseWriter, *http.Request)
现在,让我们暂时离题一下,回答一个在阅读本章时可能会出现在你脑海里面的问题:既然ListenAndServe
接受的第二个参数是一个处理器,那么为何它的默认值却是多路复用器DefaultServeMux
呢?
这是因为DefaultServeMux
多路复用器是ServeMux
结构的一个实例,而后者也拥有上面提到的ServeHTTP
方法,并且这个方法的签名与成为处理器所需的签名完全一致。换句话说,DefaultServeMux
既是ServeMux
结构的实例,也是Handler
结构的实例,因此DefaultServeMux
不仅是一个多路复用器,它还是一个处理器。不过DefaultServeMux
处理器和其他一般的处理器不同,DefaultServeMux
是一个特殊的处理器,它唯一要做的就是根据请求的URL将请求重定向到不同的处理器。在了解了这些知识之后,我们现在只需要自行编写一个处理器并使用它去代替默认的多路复用器,就可以让服务器正常地对客户端进行响应了,具体如代码清单3-6所示。
代码清单3-6 处理请求
package main
import (
"fmt"
"net/http"
)
type MyHandler struct{}
func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,"HelloWorld")
}
func main() {
handler := MyHandler{}
server := http.Server{
Addr : "127.0.0.1:8080",
Handler: &handler,
}
server.ListenAndServe()
}
现在,只要按照2.7节介绍过的方法启动服务器,并使用浏览器访问地址http://localhost:8080/
,我们就可以在浏览器里面看到Hello World
响应了。
有趣的是,如果我们使用浏览器访问http://localhost:8080/anything/at/all
,同样会看到相同的Hello World
响应。造成这个问题的原因非常明显:在代码清单3-6中,程序创建了一个处理器并将它与服务器进行了绑定,以此来代替原本正在使用的默认多路复用器。这意味着服务器不会再通过URL匹配来将请求路由至不同的处理器,而是直接使用同一个处理器来处理所有请求,因此无论浏览器访问什么地址,服务器返回的都是同样的Hello World
响应。
这也是我们在Web应用中使用多路复用器的原因:对某些特殊用途的服务器来说,只使用一个处理器也许就可以很好地完成工作了,但是在大部分情况下,我们还是希望服务器可以根据不同的URL请求返回不同的响应,而不是一成不变地只返回一种响应。