提供HTTP服务

发起HTTP请求 一文介绍了如何使用 net/http 包发起 HTTP 请求。 net/http 包同样提供了用于开发实现 HTTP 服务的基础类库,本文将进一步介绍如何使用这些类库。

先来看看一个简单的例子——计数器服务接口。 这个接口非常简单,每次均返回接口调用总次数,以 1 开始:

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

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

var counter = 0

type CounterHandler struct {}

func (handler CounterHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
    counter += 1
    fmt.Fprintln(writer, counter)
}

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

    log.Fatal(server.ListenAndServe())
}

代码中,第 9 行声明一个用于记录次数的变量 counter ,初始值为 0 ; 第 13-16 行定义请求处理器 CounterHandler 及其处理函数 ServeHTTP ,处理函数先将 counter 自增并返回响应; main 函数中第 19-22 行,申明并初始化 http.Server ,指定监听端口以及请求处理器; 第 24 调用 ListenAndServe 方法开始监听并处理网络请求。

接着启动计数器服务:

$ go run counter-server.go

通过 8080 端口即可访问该服务:

$ curl http://localhost:8080
1
$ curl http://localhost:8080
2

这是一个非常简单的程序,但不失为一个完整的 HTTP 服务。 在 net/http 包的协助下,若干行代码即可实现 HTTP 服务!

数据交换

开发 HTTP 服务,不可避免地要在客户端和服务端之间 交换数据 。 交换数据的形式非常多样,至少包括以下类型:

  • URL 值;
  • URL 参数;
  • HTTP 头部;
  • Cookie
  • POST 数据;

下面是一个非常细致的示例程序,演示服务端如何 获取请求信息 以及如何往客户端 响应数据 。 代码结构与计数器服务非常类似:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package main

import (
    "fmt"
    "log"
    "net/http"
    "io/ioutil"
)

type EchoHandler struct {}

func (handler EchoHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
    // set response header
    writer.Header().Add("X-Data", "foo")

    // set response cookie
    http.SetCookie(writer, &http.Cookie{
        Name: "x-cookie",
        Value: "bar",
        MaxAge: 86400,
        Secure: true,
    })

    writer.WriteHeader(200)

    // echo network info
    fmt.Fprintln(writer, "===== Network =====")
    fmt.Fprintln(writer, "Remote Address:", request.RemoteAddr)
    fmt.Fprintln(writer)

    // echo request line info
    fmt.Fprintln(writer, "===== Request Line =====")
    fmt.Fprintln(writer, "Method: ", request.Method)
    fmt.Fprintln(writer, "URL: ", request.URL)
    fmt.Fprintln(writer, "Host: ", request.Host)
    //fmt.Fprintln(writer, "URI: ", request.RequestURI)
    fmt.Fprintf(writer, "Protocol: %v major=%v minor=%v\n", request.Proto,
        request.ProtoMajor, request.ProtoMinor)
    fmt.Fprintln(writer)

    // echo headers
    fmt.Fprintln(writer, "===== Header =====")
    for k, v := range request.Header {
        fmt.Fprintf(writer, "%v: %v\n", k, v)
    }
    fmt.Fprintln(writer)

    // echo body
    body, err := ioutil.ReadAll(request.Body)
    if err == nil && len(body) > 0 {
        fmt.Fprintln(writer, "===== Raw Body =====")
        fmt.Fprintln(writer, string(body))
    }
}

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

    log.Println("Server starting...")
    log.Fatal(server.ListenAndServe())
}

例子第 14 行向客户端返回响应头部 X-Data ,值为: foo ; 第 17-22 行为客户端设置 Cookie ; 第 24 行,设置返回状态码并开始响应头部(后续便不能再修改响应头部了); 接着,以响应体的形式返回各种请求信息,以此演示其获取方式。

28 行,获取远端(对端)地址; 第 32-38 行,分别获取 请求方法URL主机名 ( 域名 )、 协议版本 等信息; 第 43-44 行,获取 请求头部 ; 第 49 行,读取 请求体

下一步

订阅更新,获取更多学习资料,请关注我们的 微信公众号

../_images/wechat-mp-qrcode.png

小菜学编程