Hello world!

第一步,用go语言搭建一个http版的‘hello world’程序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import (
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello world!"))
	})

	log.Println("Starting HTTP server...")
	log.Fatal(http.ListenAndServe("localhost:4000", nil))
}

三个主要函数的简单分析

  1. http.handleFunc() 函数的作用是建立路由映射。包含两个参数,第一个参数是指定的路由规则。第二个参数是,要调用的响应函数(这个函数必须符合函数签名func(http.ResponseWriter, *http.Request))。

  2. func(w http.ResponseWriter, r *http.Request) {} 这个函数同样有两个参数,第一个参数是请求所对应的响应对象http.ResponseWriter ,包括响应码,响应头,响应体等。第二个对象就是请求对应的请求对象 *http.Request,包括http请求的所有信息。 1. http.ResponseWriter 我们就是通过调用这个对象的write方法来向响应体写入字符的。

  3. http.ListenAndServe的作用是启动http服务器,并监听发送到指定地址和端口号的http请求。包含两个参数,第一个参数就是地址和端口号。第二个参数

http.handlefunc() 函数分析

​ 实际上http.HandleFunc是标准库提供的一种简便写法,他的第二个参数必须为指定格式,是因为在http.HandleFunc函数内部会将传入的绑定函数转化为类型http.Handler,

​ 而这个对象是go标准中的http请求处理器对象,该对象实现了http.Handler接口:

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

结论http.HandleFunc的根本作用就是将一个函数转化为实现了http.Handler接口的类型(http.Handler)。 那么我们就可以自己创建一个类型并实现http.Handler接口。这样就可以替代handleFunc函数

实现代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
	"log"
	"net/http"
)

func main() {
	http.Handle("/", &helloHandler{})

	log.Println("Starting HTTP server...")
	log.Fatal(http.ListenAndServe(":4000", nil))
}

type helloHandler struct{}

func (_ *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello world!"))
}

Http.ListrenAndServe(":4000”,nil)函数分析

​ 首先来分析一下第二个参数nil,根据函数声明表示,nil代替的是一个实现了http.Handler接口的对象。

1
func ListenAndServe(addr string, handler Handler) error {...}

​ 没错,这个handler就是跟上面一样,实现了handler接口的类型。我们可以直接使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import (
	"log"
	"net/http"
)

func main() {
	log.Println("Starting HTTP server...")
	log.Fatal(http.ListenAndServe("localhost:4000", &helloHandler{}))
}

type helloHandler struct{}

func (_ *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello world!"))
}

这个代码运行起来跟前面的是一样的,不过不能方便的将路由规则和函数进行绑定。

结论一ListenAndServe()的第二个参数就是一个handler,能直接用但是不能提供路由绑定。

http.ServeMux函数分析—对http.Handle剖析

通过对Hande的源码分析,Handle 是对http.ServeMux对象(http.DefaultServeMux)封装了一层。

1
2
3
func Handle(pattern string, handler Handler) {
    DefaultServeMux.Handle(pattern, handler)
}

而这个http,serveMux是go语言的带有基本路由功能的服务复用器。除了用http.HandleFunchttp.Handle这类方法操作http.DefaultServeMux之外,也可以用http.NewServeMux来创建一个新的http.ServeMux对象。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
	"log"
	"net/http"
)

func main() {
	mux := http.NewServeMux()
	mux.Handle("/", &helloHandler{})
	log.Println("Starting HTTP server...")
	log.Fatal(http.ListenAndServe("localhost:4000", mux))
}

type helloHandler struct{}

func (_ *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello world!"))
}

结论http.Handle方法,本质上是对DefaultServeMux这个对象的操作。

那么我们就可以,自己创建一个http.NewServeMux方法来对DefaultServeMux操作。

ListenAndServe是否也是对DefaultServeMux对象的封装呢

源码

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

虽然不是全局的封装,但也是用了http.server的对象。

结论:我们可以使用server写一个自定义程度较高的程序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
	"log"
	"net/http"
)

func main() {
	mux := http.NewServeMux()
	mux.Handle("/", &helloHandler{})

	server := &http.Server{
		Addr:    ":4000",
		Handler: mux,
	}
	log.Println("Starting HTTP server...")
	log.Fatal(server.ListenAndServe())
}

type helloHandler struct{}

func (_ *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello world!"))
}