一、介绍

go作为一个对web开发友好的语言,内置了net/http库。来帮助我们实现web服务器。
我们来看官方文档给我的最简单的一个文件服务器的实现。

func main() {
    // Simple static webserver:
    handler := http.FileServer(http.Dir("/usr/share/doc"))
    _ = http.ListenAndServe(":8080", handler)
}

发现创建一个服务器主要有两步:

  1. 创建路由
  2. 启用服务器,并监听一个web端口

在启用服务器,并监听端口,我们一般用http.ListenAndServe这个方法。
在源码

// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

我们可以看到ListenAndServe主要传两个参数,端口号,以及handle路由。
我们可以使用 http.Handle()或者http.HandleFunc()来创建路由。
注册路由的话,我们主要有使用默认的DefaultServeMux,或者自己创建ServeMux。

二、使用DefaultServeMux创建路由的三种方式。

我们看一下Handler接口的定义

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

只要实现了 Handler 接口,就是一个有效的路由注册。
在官方默认的DefaultServeMux我们主要实现了三种方式,来看第一种。

2.1 通过官方默认类型fileHandler来创建

比如我们开始使用的,用来创建一个 静态文件web服务器的方法。

func main() {
    // Simple static webserver:
    handler := http.FileServer(http.Dir("/usr/share/doc"))
    _ = http.ListenAndServe(":8080", handler)
}

主要是定义一个结构体,来实现Handler接口,如 http.FileServer()
就是用来创建了一个结构体fileHandler,而这个fileHandler拥有ServeHTTP(w ResponseWriter, r *Request)方法。
源码如下:net/http/fs.go

func FileServer(root FileSystem) Handler {
    return &fileHandler{root}
}

func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
    upath := r.URL.Path
    if !strings.HasPrefix(upath, "/") {
        upath = "/" + upath
        r.URL.Path = upath
    }
    serveFile(w, r, f.root, path.Clean(upath), true)
}

2.2 http.Handle函数+自定义类型

在官方文档https://pkg.go.dev/net/http#Handle上,我们可以看到官方的例子。

import (
    "fmt"
    "log"
    "net/http"
    "sync"
)

type countHandler struct {
    mu sync.Mutex // guards n
    n  int
}

func (h *countHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    h.mu.Lock()
    defer h.mu.Unlock()
    h.n++
    fmt.Fprintf(w, "count is %d\n", h.n)
}

func main() {
    http.Handle("/count", new(countHandler))
    log.Fatal(http.ListenAndServe(":8080", nil))
}

可以看到主要分三步,可以实现一个自定义类型的路由。

  1. 创建一个自定义类型
  2. 实现ServeHTTP(w http.ResponseWriter, r *http.Request)
  3. http.Handle()注册路由

2.3 使用HandleFunc 创建路由

这个方法是最简单的,相比第二种方法,我们可以先看下。

package main

import (
    "fmt"
    "net/http"
)

func index(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello World")
}

func main() {
    http.HandleFunc("/index", index)
    http.ListenAndServe(":8080", nil)
}

我们可以打开网页 http://localhost:8080/index
发现非常简单的就实现了一个 Hello World的输出。

我们在这里,包括第二种方法,调用http.ListenAndServe()的时候,在handle参数的时候,都传了一个nil。
原因是使用默认的 DefaultServeMux。
可以看一下 http.HandleFunc()的源码 src/net/http/server,go

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}

在底层调用了DefaultServeMux。
我们使用 net/http库,在这个库里面,有一个全局的变量DefaultServeMux。
即使当http.ListenAndServe()我们这里handle参数不传的时候,就会调用默认的DefaultServeMux。
调用http.HandleFunc()/http.Handle()都是将处理器/函数注册到ServeMux的默认对象DefaultServeMux上。


海生
104 声望33 粉丝

与黑夜里,追求那一抹萤火。