Skip to content

Go Web 编程 #3

@xtyi

Description

@xtyi

我们首先来写一个最简单的 Web 服务器,代码仅仅调用了 http.ListenAndServe 函数

package main

import "net/http"

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

http.Server

我们也可以创建一个 http.Server 对象,调用其 ListenAndServe 方法来开启 Web 服务器,这两种方式是等价的

package main

import "net/http"

func main() {
    server := http.Server{
        Addr:    ":8080",
        Handler: nil,
    }
    server.ListenAndServe()
}

实质上 http.ListenAndServe 函数只是一个语法糖,在内部就是创建了 http.Server 和调用其 ListenAndServe 方法

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

当不需要太多配置时,可以使用该函数,而想更多的自定义配置的话,就手动创建 http.Server,其定义如下

type Server struct {
    Addr      string
    Handler    Handler
  
    ReadTimeout  time.Duration
    ReadHeaderTimeout time.Duration
    WriteTimeout  time.Duration
    IdleTimeout time.Duration
  
    MaxHeaderBytes int
    TLSConfig   *tls.Config
    TLSNextProto  map[string]func(*Server, *tls.Conn, Handler)
    ConnState   func(net.Conn, ConnState)
    ErrorLog    *log.Logger
}

创建 http.Server 最重要的两个参数是 AddrHandler

但是上面调用 http.ListenAndServe 函数或是创建 http.Server 时,都将 Handler 设置为了 nil

Handlernil 时,服务器会使用默认的多路复用器 DefaultServeMux

多路复用器之后再说,我们先来创建自己的处理器

http.Handler

Handler 是一个接口,定义如下

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

因此我们只要创建一个结构体,实现 ServeHTTP(ResponseWriter, *Request) 方法即可,该方法用来处理 HTTP 请求,代码如下

package main

import (
    "fmt"
    "net/http"
)

type MyHandler struct {}

func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World!")
}

func main() {
    handler := &MyHandler{}
    server := http.Server{
        Addr: "127.0.0.1:8080",
        Handler: handler,
    }
    server.ListenAndServe()
}

http.Request

http.Request 是一个结构体,保存了请求相关的各种信息

Web 编程中经常要获取的 URL 信息,Header 信息,表单信息就通过该参数获取

type Request struct {
    Method string
    URL *url.URL
    Proto
    ProtoMajor int
    ProtoMinor int
    Header Header
    Body io.ReadCloser
    GetBody func() (io.ReadCloser, error)
    ContentLength int64
    TransferEncoding []string
    Close bool
    Host string
    Form url.Values
    PostForm url.Values
    MultipartForm *multipart.Form
    Trailer Header
    RemoteAddr string
    RequestURI string
    TLS *tls.ConnectionState
    Cancel <-chan struct{}
    Response *Response
} 

获取指定 Header 字段的值

r.Header["Accept-Encoding"] // 返回 []string, eg: ["gzip", "deflate", "br"]
r.Header.Get("Accept-Encoding") // 返回 string, eg: "gzip, deflate, br"

http.ResponseWriter

http.ResponseWriter 是一个接口,用来向 HTTP 响应报文的 Body 写入数据

type ResponseWriter interface {
    Write([]byte) (int, error)
    Header() Header
    WriteHeader(statusCode int)
}

输出 Hello World 的方法有三种,如下

w.Write([]byte("Hello World"))

fmt.Fprintf(w, "Hello World")

io.WriteString(w, "Hello World")

第一种不必说,直接调用 Write 方法,但这里需要将 string 转换成 byte[]

剩下两种方法其实都在内部使用了 Write 方法,如下

func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
    p := newPrinter()
    p.doPrintf(format, a)
    n, err = w.Write(p.buf)
    p.free()
    return
}
func WriteString(w Writer, s string) (n int, err error) {
    if sw, ok := w.(stringWriter); ok {
        return sw.WriteString(s)
    }
    return w.Write([]byte(s))
}

这里两个函数接收的参数是 io.Writer 类型,该类型是一个接口,如下

type Writer interface {
    Write(p []byte) (n int, err error)
}

http.ResponseWriter 接口也定义了相同的 Write 方法,所以实现了 http.ResponseWriter 接口的类型,必定也实现了 io.Writer 接口,因此可以使用上述两个函数

http.ServeMux

我们现在开启了一个 HTTP 服务器,然后写了一个 Handler 来处理 HTTP 请求,向页面输出 Hello World

但这远远不够,至少我们要能对不同的 URL 调用不同的函数去处理,我们需要路由功能

多路复用器 http.ServeMux 就提供了这样的功能,它负责接收 HTTP 请求并根据请求中的 URL 来重定向到正确的 Handler

当然,http.ServeMux 本身也是一个 Handler,实现了 ServeHTTP 方法,其定义及方法如下

type ServeMux struct {}

func NewServeMux() *ServeMux
func (mux *ServeMux) Handle(pattern string, handler Handler)
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)

接下来我们创建两个 Handler,并使用默认的多路复用器分发请求到两个处理器

package main

import (
    "fmt"
    "net/http"
)

type FooHandler struct {}

type BarHandler struct {}

func (h *FooHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "foo")
}

func (h *BarHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "bar")
}

func main() {
    foo := &FooHandler{}
    bar := &BarHandler{}
    server := http.Server{
        Addr: "127.0.0.1:8080",
    }
    http.Handle("/foo", foo)
    http.Handle("/bar", bar)
    server.ListenAndServe()
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions