提供HTTP服务

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

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

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "log"
  6. "net/http"
  7. )
  8.  
  9. var counter = 0
  10.  
  11. type CounterHandler struct {}
  12.  
  13. func (handler CounterHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
  14. counter += 1
  15. fmt.Fprintln(writer, counter)
  16. }
  17.  
  18. func main() {
  19. server := &http.Server{
  20. Addr: ":8080",
  21. Handler: CounterHandler{},
  22. }
  23.  
  24. log.Fatal(server.ListenAndServe())
  25. }

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

接着启动计数器服务:

  1. $ go run counter-server.go

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

  1. $ curl http://localhost:8080
  2. 1
  3. $ curl http://localhost:8080
  4. 2

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

数据交换

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

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

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

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "io/ioutil"
  8. )
  9.  
  10. type EchoHandler struct {}
  11.  
  12. func (handler EchoHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
  13. // set response header
  14. writer.Header().Add("X-Data", "foo")
  15.  
  16. // set response cookie
  17. http.SetCookie(writer, &http.Cookie{
  18. Name: "x-cookie",
  19. Value: "bar",
  20. MaxAge: 86400,
  21. Secure: true,
  22. })
  23.  
  24. writer.WriteHeader(200)
  25.  
  26. // echo network info
  27. fmt.Fprintln(writer, "===== Network =====")
  28. fmt.Fprintln(writer, "Remote Address:", request.RemoteAddr)
  29. fmt.Fprintln(writer)
  30.  
  31. // echo request line info
  32. fmt.Fprintln(writer, "===== Request Line =====")
  33. fmt.Fprintln(writer, "Method: ", request.Method)
  34. fmt.Fprintln(writer, "URL: ", request.URL)
  35. fmt.Fprintln(writer, "Host: ", request.Host)
  36. //fmt.Fprintln(writer, "URI: ", request.RequestURI)
  37. fmt.Fprintf(writer, "Protocol: %v major=%v minor=%v\n", request.Proto,
  38. request.ProtoMajor, request.ProtoMinor)
  39. fmt.Fprintln(writer)
  40.  
  41. // echo headers
  42. fmt.Fprintln(writer, "===== Header =====")
  43. for k, v := range request.Header {
  44. fmt.Fprintf(writer, "%v: %v\n", k, v)
  45. }
  46. fmt.Fprintln(writer)
  47.  
  48. // echo body
  49. body, err := ioutil.ReadAll(request.Body)
  50. if err == nil && len(body) > 0 {
  51. fmt.Fprintln(writer, "===== Raw Body =====")
  52. fmt.Fprintln(writer, string(body))
  53. }
  54. }
  55.  
  56. func main() {
  57. server := &http.Server{
  58. Addr: ":8080",
  59. Handler: EchoHandler{},
  60. }
  61.  
  62. log.Println("Server starting...")
  63. log.Fatal(server.ListenAndServe())
  64. }

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

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

下一步

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

../_images/wechat-mp-qrcode.png小菜学编程

参考文献