Http 是一个比 tcp 更高级的协议,它描述了客户端浏览器如何与网页服务器进行通信。Go 有自己的 net/http 包,我们来看看它。我们从一些简单的示例开始,
    首先编写一个 “Hello world!”:查看示例 15.6

    1. package main
    2. import (
    3. "fmt"
    4. "log"
    5. "net/http"
    6. )
    7. func HelloServer(w http.ResponseWriter, req *http.Request) {
    8. fmt.Println("Inside HelloServer handler")
    9. fmt.Fprintf(w, "Hello,"+req.URL.Path[1:])
    10. }
    11. func main() {
    12. http.HandleFunc("/", HelloServer)
    13. err := http.ListenAndServe("localhost:8080", nil)
    14. if err != nil {
    15. log.Fatal("ListenAndServe: ", err.Error())
    16. }
    17. }

    我们引入了 http 包并启动了网页服务器,和 15.1 的 net.Listen("tcp", "localhost:50000") 函数的 tcp 服务器是类似的,使用 http.ListenAndServe("localhost:8080", nil) 函数,如果成功会返回空,否则会返回一个错误(可以指定 localhost 为其他地址,8080 是指定的端口号)

    http.URL 描述了 web 服务器的地址,内含存放了 url 字符串的 Path 属性;http.Request 描述了客户端请求,内含一个 URL 属性

    如果 req 请求是一个 POST 类型的 html 表单,“var1” 就是 html 表单中一个输入属性的名称,然后用户输入的值就可以通过 GO 代码:req.FormValue("var1") 获取到(请看章节 15.4)。还有一种方法就是先执行 request.ParseForm() 然后再获取 `request.Form [“var1”] 的第一个返回参数,就像这样:

    1. var1, found := request.Form["var1"]

    第二个参数 found 就是 true,如果 var1 并未出现在表单中,found 就是 false

    表单属性实际上是一个 map[string][]string 类型。网页服务器返回了一个 http.Response,它是通过 http.ResponseWriter 对象输出的,这个对象整合了 HTTP 服务器的返回结果;通过对它写入内容,我们就将数据发送给了 HTTP 客户端。

    现在我们还需要编写网页服务器必须执行的程序,它是如何处理请求的呢。这是在 http.HandleFunc 函数中完成的,就是在这个例子中当根路径 “/”(url 地址是 localhost:8080 )被请求的时候(或者这个服务器上的其他地址),HelloServer 函数就被执行了。这个函数是 http.HandlerFunc 类型的,它们通常用使用 Prehandler 来命名,在前边加了一个 Pref 前缀。

    http.HandleFunc 注册了一个处理函数 (这里是 HelloServer) 来处理对应 / 的请求。

    1. /` 可以被替换为其他特定的 url 比如 `/create`,`/edit` 等等;你可以为每一个特定的 url 定义一个单独的处理函数。这个函数需要两个参数:第一个是 `ReponseWriter` 类型的 `w`;第二个是请求 `req`。程序向 `w` 写入了 `Hello` 和 `r.URL.Path[1:]` 组成的字符串后边的 `[1:]` 表示 “创建一个从第一个字符到结尾的子切片”,用来丢弃掉路径开头的 “/”,`fmt.Fprintf()` 函数完成了本次写入(请看[章节 12.8](https://go.learnku.com/docs/the-way-to-go/an-actual-example-of-128-using-the-interface-fmtfprintf/112));另外一种写法是 `io.WriteString(w, "hello, world!\n")

    总结:第一个参数是请求的路径,第二个参数是处理这个路径请求的函数的引用。

    示例 15.6 hello_world_webserver.go

    1. package main
    2. import (
    3. "fmt"
    4. "log"
    5. "net/http"
    6. )
    7. func HelloServer(w http.ResponseWriter, req *http.Request) {
    8. fmt.Println("Inside HelloServer handler")
    9. fmt.Fprintf(w, "Hello,"+req.URL.Path[1:])
    10. }
    11. func main() {
    12. http.HandleFunc("/", HelloServer)
    13. err := http.ListenAndServe("localhost:8080", nil)
    14. if err != nil {
    15. log.Fatal("ListenAndServe: ", err.Error())
    16. }
    17. }

    使用命令行启动程序,会打开一个命令窗口显示如下文字:

    1. Starting Process E:/Go/GoBoek/code_examples/chapter_14/hello_world_webserver.exe
    2. ...

    然后打开你的浏览器并输入 url 地址:http://localhost:8080/world,浏览器就会出现文字:Hello, world,网页服务器会响应你在:8080/ 后边输入的内容

    使用 fmt.Println 在控制台打印状态,在每个 handler 被请求的时候,在他们内部打印日志会很有帮助

    注:
    1)前两行(没有错误处理代码)可以替换成以下写法:

    1. http.ListenAndServe(":8080", http.HandlerFunc(HelloServer))

    2)fmt.Fprintfmt.Fprintf 都是用来写入 http.ResponseWriter 的不错的函数(他们实现了 io.Writer)。
    比如我们可以使用

    1. fmt.Fprintf(w, "<h1>%s<h1><div>%s</div>", title, body)

    来构建一个非常简单的网页并插入 titlebody 的值

    如果你需要更多复杂的替换,使用模板包(请看章节 15.7
    3)如果你需要使用安全的 https 连接,使用 http.ListenAndServeTLS() 代替 http.ListenAndServe()
    4)http.HandleFunc("/", Hfunc) 中的 HFunc 是一个处理函数,如下:

    1. func HFunc(w http.ResponseWriter, req *http.Request) {
    2. ...
    3. }

    也可以使用这种方式:http.Handle("/", http.HandlerFunc(HFunc))

    上边的 HandlerFunc 只是一个类型名称,它定义如下:

    1. type HandlerFunc func(ResponseWriter, *Request)

    它是一个可以把普通的函数当做 HTTP 处理器的适配器。如果 f 函数声明的合适,HandlerFunc(f) 就是一个执行了 f 函数的处理器对象。

    http.Handle 的第二个参数也可以是 T 的一个 obj 对象:http.Handle("/", obj) 给 T 提供了 ServeHTTP 方法,实现了 http 的 Handler 接口:

    1. func (obj *Typ) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    2. ...
    3. }

    这个用法在 章节 15.8 类型 CounterChan 上使用过。只要实现了 http.Handlerhttp 包就可以处理任何 HTTP 请求。

    练习 15.2:webhello2.go

    编写一个网页服务器监听端口 9999,有如下处理函数:

    • 当请求 http://localhost:9999/hello/Name 时,响应:hello Name(Name 需是一个合法的姓,比如 Chris 或者 Madeleine)
    • 当请求 http://localhost:9999/shouthello/Name 时,响应:hello NAME

    练习 15.3:hello_server.go

    创建一个空结构 hello 并使它实现 http.Handler。运行并测试。