01
介绍
在 Golang 语言中,可以使用 net/http
实现 http server,可以通过调用 ListenAndServe
函数,传入给定参数,地址和处理器 (handler
)。处理器参数为 nil
时,默认使用 DefaultServeMux
。
也可以使用 net/http
标准库实现 http client。可以通过调用 Get
,Head
,Post
,和 PostForm
函数发送 http(或 https) 请求。需要注意的是,客户端完成请求后,必须关闭响应主体。
02
Server
使用 net/http
标准库实现 http server,通常有两种方式,分别是使用处理器和使用处理器函数。
处理器 Handler
其中,处理器方式包含单个处理器和多个处理器。
单个处理器,示例代码:
func main () {
hello := Hello{name: "frank"}
httpServer := http.Server{Addr: ":8080", Handler: &hello}
httpServer.ListenAndServe()
}
type Hello struct {
name string
}
func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello " + h.name)
}
阅读上面这段代码,我们发现定义一个结构体类型的变量,通过实现 ServeHTTP(ResponseWriter, *Request)
方法,创建 Handler,并将其赋值给 http.Server
的 Handler 字段。
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
阅读源码,可以发现 Handler 是包含一个 ServeHTTP(ResponseWriter, *Request)
方法的接口。
使用单个处理器的方式,方便传参,但是,不可以匹配多个路由,所以如果需要匹配多个路由,我们可以使用多个处理器的方式。
多个处理器,示例代码:
func main () {
httpServer := http.Server{
Addr: ":8080",
}
http.Handle("/hello", Hello{})
http.Handle("/world", World{})
httpServer.ListenAndServe()
}
type Hello struct {}
type World struct {}
func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello")
}
func (w1 World) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "world")
}
阅读上面这段代码,可以发现,我们创建了多个处理器,然后调用 http.Handle
函数,将处理器注册到 DefaultServeMux
,多路复用器将路由请求转发给该路由的 ServerHTTP 方法处理。
使用多个处理器的方式,可以一个处理器匹配一个路由,通过创建多个处理器,可以匹配多个路由。
处理器函数
处理器方式,一个路由对应一个处理器(handler),当我们有多个路由时,需要创建多个处理器,使用上有些繁琐。因此,net/http
标准库提供了一个函数 HandleFunc
,它可以将传参的路由和 handler func 注册到 DefaultServeMux
。
handler func 也实现了 Handler
结构体的 ServeHTTP(ResponseWriter, *Request)
方法。
处理器函数(handler func),示例代码:
func main () {
http.HandleFunc("/hello", hello)
http.HandleFunc("/world", world)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal(err)
}
}
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello")
}
func world(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "world")
}
阅读上面这段代码,我们发现使用处理器函数的方式,相比使用处理器的方式,代码量减少了。但是,传参就变得不优雅了,我们在使用时可以根据场景择优选择。
聪明的读者朋友们可能发现,以上示例中使用的是 DefaultServeMux
,为什么没有使用函数 http.NewServeMux
创建一个新的多路复用器?
因为 net/http
标准库会默认创建 DefaultServeMux
。函数 http.Handle
和 http.HandleFunc
将处理器注册到 DefaultServeMux
,在 ListenAndServe
未接收到 handler 参数时,默认使用 DefaultServeMux
。
源码如下:
func NewServeMux() *ServeMux { return new(ServeMux) }
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
我们也可以创建一个新的多路复用器,管理路由请求,包含处理器模式和处理器函数模式。
处理器模式,示例代码:
func main () {
serveMux := http.NewServeMux()
hello := Hello{}
serveMux.Handle("/hello", hello)
http.ListenAndServe(":8080", serveMux)
}
type Hello struct {}
func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello")
}
阅读上面这段代码,使用创建的多路复用器,调用多路复用器的 Handle 方法注册处理器(Handler)。
处理器函数模式,示例代码:
func main () {
serveMux := http.NewServeMux()
hello := http.HandlerFunc(Hello)
serveMux.Handle("/hello", hello)
http.ListenAndServe(":8080", serveMux)
}
func Hello (w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello")
}
阅读上面这段代码,使用函数 http.HandlerFunc
将传入函数类型的参数转义为 HandlerFunc。
03
Client
使用 net/http
标准库实现 http client。可以通过调用 Get
,Head
,Post
,和 PostForm
函数发送 http(或 https) 请求。需要注意的是,客户端完成请求后,必须关闭响应主体。
示例代码:
func main () {
http.HandleFunc("/client", client)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal(err)
}
}
func client(w http.ResponseWriter, r *http.Request) {
resp, err := http.Get("http://127.0.0.1:8080/hello")
if err != nil {
log.Fatal(err)
}
data, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
fmt.Fprintln(w, string(data))
}
04
Request Param
关于接收请求参数,http.Request
也提供了一些函数和方法,限于篇幅,本文仅提供一个示例,对此不准备过多介绍。
func main () {
http.HandleFunc("/param1", param1)
http.HandleFunc("/param2", param2)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal(err)
}
}
func param1(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Fprintln(w, r.Form["user"][0])
}
func param2(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, r.FormValue("user"), r.FormValue("age"))
}
阅读上面这段代码,我们定义了两个接收请求参数的函数,分别通过 r.ParseForm
和 r.FormValue
方法获取请求参数,其中 r.ParseFome
方法是将参数解析到 r.Form 中,r.FormValue
方法直接返回一个字符串类型的给定参数 key 的第一个值。关于更多使用方式,请阅读官方文档。
05
Response Result
关于响应结果,http.ResponseWriter
接口也提供了三个方法:
type ResponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(statusCode int)
}
限于篇幅,在此也不做过多介绍,仅提供一个示例。关于更多使用方式,请阅读官方文档。
func main () {
http.HandleFunc("/json", jsonRes)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal(err)
}
}
func jsonRes(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
ageStr := r.FormValue("age")
ageInt, err := strconv.Atoi(ageStr)
if err != nil {
log.Fatal(err)
}
age := uint8(ageInt)
username := r.FormValue("user")
jsonData, err := json.Marshal(struct {
Username string
Age uint8
}{username, age})
if err != nil {
log.Fatal(err)
}
fmt.Fprintln(w, string(jsonData))
}
06
总结
本文我们主要介绍使用 net/http
标准库实现 http server 和 http client 的使用方式。并简单列举了请求参数和响应结果的使用示例。关于 Cookie 的操作,在之前的公众号文章中介绍过,本文也没有重复赘述。
重点需要掌握的是处理器和多路复用器,包括处理器和处理器函数,以及新建多路复用器和默认多路复用器。此外,net/http
标准库还有几个内置函数,可以作为处理器,比如函数 NotFoundHandler
和 RedirectHandler
等。
阅读完本文,读者朋友们应该已经了解 Golang 语言怎么使用 net/http 标准库开发 http 应用。
推荐阅读: