第一种:最简单的
package main
import (
"fmt"
"log"
"net/http"
)
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello http server!")
}
func main() {
http.HandleFunc("/", myHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
上面的变种1:
//上面的一个变种
package main
import (
"fmt"
"log"
"net/http"
)
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello http server 1!")
}
func main() {
//// 通过 HandlerFunc 把函数转换成 Handler 接口的实现对象
handler := http.HandlerFunc(myHandler)
http.Handle("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
ListenAndServe函数负责监听并处理连接。
第二种:Handler接口
上面的那种方式发挥余地太小,比如我想设置server的Timeout时间都不能设置。这时候我们就用到了 自定义的server
type Server struct {
Addr string //TCP address to listen on
Handler Handler //handler to invoke
ReadTimeout time.Duration //maximum duration before timing out read of the request
WriteTimeout time.Duration //maximum duration before timing out write of the response
TLSConfig *tls.Config
...
}
//Handler是一个interface,定义如下
type Handler interface {
ServeHTTP(ResponseWrite, *Request)
}
所以只要我们实现了Handler接口的方法ServeHTTP就可以自定义我们的server了。示例代码如下
package main
import (
"fmt"
"log"
"net/http"
)
type myHandler struct{}
func (myHandler myHandler) ServeHTTP(w ResponseWrite, r *Request) {
fmt.Fprintf(w, "hello serverhttp 1")
}
func main() {
server := http.Server{
Addr: ":8080",
Handler: &myHandler{},
ReadTimeout: 3 * time.Second,
}
log.Fatal(server.ListenAndServe(":8080", nil))
}
还有下面这种:
package main
import (
"fmt"
"log"
"net/http"
)
type myHandler struct{}
func (myHandler *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello, server 2!")
}
func main() {
http.Handle("/", &myHandler{})
log.Fatal(http.ListenAndServe(":8080", nil))
}
还有这种:
package main
import (
"fmt"
"log"
"net/http"
)
type MyHandler struct{}
func (myHandler *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/":
fmt.Fprintf(w, "URL.Path = %q
", r.URL.Path)
case "/hello":
for k, v := range r.Header {
fmt.Fprintf(w, "Header[%q] = %q
", k, v)
}
default:
fmt.Fprintf(w, "404 not found: %s
", r.URL)
}
}
func main() {
myHandler := new(MyHandler)
log.Fatal(http.ListenAndServe(":8080", myHandler))
}
第三种:多个url的ServeMux
ServeMux可以注册多个URL和handler的对应关系,并自动把请求转移到对应的handler进行处理。
package main
import (
"fmt"
"log"
"net/http"
)
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello server!")
}
func yourHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "your handler")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/my", myHandler)
mux.HandleFunc("/your", yourHandler)
log.Fatal(http.ListenAndServe(":8080", mux))
}
- 通过 NewServeMux 生成了 ServerMux 结构,URL 和 handler 是通过它注册的
- http.ListenAndServe 方法第二个参数变成了上面的 mux 变量
思考:上面写的那些程序是怎么实现的?
一个HTTP请求过程要认识几个概念,服务器端的几个重要概念:
- Request: 用户请求的信息,这个可以用来处理用户的请求信息,POST,url,cookie,header等信息
- Response:服务器返回给用户的信息
- Conn:每次连接的信息
- Handler:处理请求和生成返回处理信息的逻辑
url路由到处理函数是怎么实现的?就是上面所说的Handler的处理逻辑。
我们先来看看golang自带的包 net/http 下的程序,主要程序在 net/http/server.go 文件中。
经过追踪程序,去掉其他的细节末节,处理url到handler的一个最重要函数是 func (mux *ServeMux) Handle(pattern string, handler Handler)
,即使是func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
这个处理函数,最后也是Handle函数来处理的,看看它们具体的代码:
//具体处理url路由到Handler
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
if pattern == "" {
panic("http: invalid pattern")
}
if handler == nil {
panic("http: nil handler")
}
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
}
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
e := muxEntry{h: handler, pattern: pattern}
mux.m[pattern] = e
if pattern[len(pattern)-1] == '/' {
mux.es = appendSorted(mux.es, e)
}
if pattern[0] != '/' {
mux.hosts = true
}
}
// HandleFunc registers the handler function for the given pattern.
//路由url到HandleFunc,与上面的函数(func (mux *ServeMux) Handle(pattern string, handler Handler))有什么区别?
//这个HandleFunc是具体的处理函数,而上面的Handle函数是一个接口,一个类型只要实现接口里面的方法,那么就可以传入进来作为Handler进行处理
//我的理解就是提供了2中不同的处理url路由的方法
//其实这个函数最后处理还是用了上面的Handle()方法
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
有了处理函数,肯定会有另外一个疑问,url是怎么映射到处理函数的呢?其实我们从上面方法就可以看出来,方法有一个接收体 mux *ServeMux
,看看这个具体代码是什么?
// 一个结构体,存储了url,也存储了对于的Handler,在muxEntry里
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
// 定义了存储的路由和根据路由来处理逻辑的Handler
type muxEntry struct {
h Handler //根据路由具体的处理接口
pattern string //定义的路由
}
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
上面的muxEntry struct
结构体有一个h字段,他是 Handler接口,它具体是什么呢?
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
它是处理HTTP请求的一个接口。这也是设计好程序的一个方法,依赖接口,而不是具体的实现。
也就是说只要实现了这个接口里的方法ServeHTTP(ResponseWriter, *Request),就是处理HTTP请求了。
包里面就有一个这样的程序:
//装饰器模式
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r). 实现了 Handler interface里的方法
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
包里还定义了默认处理方法,如下
func Handle(pattern string, handler Handler) {
DefaultServeMux.Handle(pattern, handler)
}
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
其实我们看看上面的方法,是不是调用的最开始我们分析的DefaultServeMux.Handle(pattern, handler)
和 DefaultServeMux.HandleFunc(pattern, handler)
2个处理函数 Handle
和 HandleFunc
。
包里有默认处理函数,那我们能不能自定义函数处理呢?
答案:当然可以的。你看 第二种:Handler接口 就是自定义的处理函数,因为我们定义一个类型struct,然后它实现了Handler 接口方法 ServeHTTP , 而处理url路由到Handler的方法第二个参数就是Handler接口,
看看方法:func (mux *ServeMux) Handle(pattern string, handler Handler)
。 所以当然是可以的。
这也是一个依赖接口设计的好处,不仅官方包可以写处理函数,我们也可以自定义处理函数。但是
Handle
函数参数不用修改。只要传一个实现了接口方法的类型数据就可以了。
update: 2020.01.20 晚