前言

这两天懒了(bushi),快赶赶进度…
学习来源ChatGPT&&Go by example
本文会包括go的net/http标准库等基础知识!零基础小白请放心使用!
会有涉及计网的一点知识 (本人也正在cs144里挣扎)
好难过下周要考计组和数据结构
长文预警!!!

Go的net/http标准库

基本概念

  • net/http 是什么
    它是 Go 标准库中用来处理 HTTP 请求和响应的模块,提供了构建 Web 服务的基本功能。
  • 核心组件
    • http.Server:表示 HTTP 服务器。
    • http.Request:表示 HTTP 请求。
    • http.ResponseWriter:用于构造 HTTP 响应。
    • 路由与处理器:通过 Handler 接口和 ServeMux 路由器来实现。

示例

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

import (
"fmt"
"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) // 响应内容
}

func main() {
http.HandleFunc("/", handler) // 注册处理函数
fmt.Println("Server is running on http://localhost:8080")
http.ListenAndServe(":8080", nil) // 启动服务器
}

运行后,访问 http://localhost:8080/Go,浏览器将显示:Hello, Go!

分块详细解释

启动HTTP服务器

http.ListenAndServe

用于启动HTTP服务器

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
http.ListenAndServe(":8080", nil)
```

- 参数:
- :8080:监听的端口。
- nil:表示使用默认的路由器(http.DefaultServeMux)。

#### 自定义``http.Server``

在 Go 的 net/http 标准库中,http.Server 是一个结构体,它表示一个 HTTP 服务器。相比直接使用 http.ListenAndServe,通过自定义 http.Server,可以对服务器的行为进行更细粒度的控制,比如设置超时时间、TLS 配置、监听地址等。

##### ``http.Server``的结构

```golang
type Server struct {
Addr string // 监听地址(如 ":8080")
Handler http.Handler // 请求处理器,默认是 http.DefaultServeMux
ReadTimeout time.Duration // 读取请求头的最大时间
WriteTimeout time.Duration // 写入响应的最大时间
IdleTimeout time.Duration // 连接空闲时间(HTTP/1.1 的 Keep-Alive)
MaxHeaderBytes int // 请求头的最大字节数
TLSConfig *tls.Config // TLS 配置(用于 HTTPS)
ConnState func(net.Conn, ConnState) // 连接状态回调
ErrorLog *log.Logger // 自定义错误日志
}

请求处理器(Handler)

基本概念

Handler 是 Go 中处理 HTTP 请求的一个接口,它定义了以下方法:

1
2
3
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
  • ResponseWriter:
    用于构建和发送 HTTP 响应。
    通过它可以设置状态码、响应头以及响应体。
  • Request:
    表示客户端的 HTTP 请求。
    包含请求方法、URL、头信息、表单数据等。

总结:Handler 是处理 HTTP 请求并返回 HTTP 响应的逻辑核心。

实现Handler的两种方式

(1)实现http.Handler接口

任何实现了ServeHTTP(w http.ResponseWriter, r *http.Request)方法的类型都可以作为一个 Handler。

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
package main

import (
"fmt"
"net/http"
)

type myHandler struct{}

func (h *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//h *myHandler是一个指针接收者,表示ServeHTTP 方法与 myHandler 类型的指针绑定
//http.ResponseWriter 是一个接口,用于构造并发送 HTTP 响应给客户端
//*http.Request 是一个指向请求对象的指针,封装了客户端发来的 HTTP 请求的全部信息
fmt.Fprintf(w, "Hello, you've hit %s\n", r.URL.Path)
//r.URL 是标准库 net/url 定义的 url.URL 结构体,用来解析和存储 URL 的详细信息。
//fmt.Fprintf 是 Go 语言中的一个非常常用的函数,它属于 fmt 包
//用于将格式化的字符串输出到指定的 io.Writer 接口对象中,而不是直接输出到控制台。
//与 fmt.Printf 和 fmt.Println 等标准输出函数不同,fmt.Fprintf 可以将输出定向到文件、网络连接、HTTP 响应等任何实现了 io.Writer 接口的对象。
}

func main() {
handler := &myHandler{}
http.ListenAndServe(":8080", handler)
//func ListenAndServe(addr string, handler http.Handler) error
//addr string指定服务器的监听地址,包括 IP 和端口号。
//handler http.Handler处理 HTTP 请求的对象,通常是一个实现了 http.Handler 接口的实例。
//启动一个HTTP服务端,持续监听8080端口,直到收到客户端请求
//如果没有指定 IP 地址或域名,那么访问的默认是本机
//当浏览器发起请求(通过 TCP 协议连接到 localhost:8080)时,服务器接收这个连接
//解析收到的 HTTP 请求,将请求分发给注册的处理器 handler
}

在terminal中键入go run main.go,然后在浏览器中键入http://localhost:8080/hello,得到Hello, you've hit /hello

这个过程中,浏览器相当于客户端,浏览器通过url(http://localhost:8080/hello)构造出http请求,然后通过DNS解析把域名换算成ip地址,这样客户端知道目标服务器是谁,然后建立tcp/ip连接,再把构造出的http请求发送到目标服务器,在这里也就是把请求发送到本机的8080端口。Go程序相当于服务端,收到并解析http请求,把请求分发给注册的处理器handler。

在 Go 中,http 包提供了一个非常核心的功能:通过 HTTP 路由将不同的请求(通常是 HTTP 方法和路径的组合)指向特定的处理器(Handler)。这个过程叫做 注册。

(2)使用 http.HandlerFunc

Go 提供了一个便利的方法,可以将普通函数直接用作 Handler。这是通过类型 http.HandlerFunc 实现的

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

import (
"fmt"
"net/http"
)

func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, you've hit %s\n", r.URL.Path)
}

func main() {
http.HandleFunc("/", myHandler) // 直接注册处理函数
http.ListenAndServe(":8080", nil)
}

访问http://localhost:8080/test将返回Hello, you've hit /test

如何实现的?
http.HandleFunc 的底层实现会将你传递的函数(如 myHandler)转为一个匿名的结构体,并为这个结构体实现 ServeHTTP 方法。这个匿名结构体的 ServeHTTP 方法就是你提供的 myHandler 函数。可以理解为 http.HandleFunc 自动为你完成了接口的实现。

HTTP 请求对象 (http.Request)

http.Request 是 Go 中处理 HTTP 请求的核心结构体,包含了许多关于请求的详细信息,包括请求方法、URL、头部、请求体等。通过这个结构体,你可以访问并处理各种与 HTTP 请求相关的数据,进而实现 HTTP 服务的功能。

在本地显示读取控制。

http.Request的主要字段(仅举例两个)

  1. Method(请求方法)

    • 类型:string
    • 描述:表示 HTTP 请求的类型,如 GET、POST、PUT、DELETE 等。
    • 例如,如果是一个 GET 请求,Method 会是 “GET”,如果是一个 POST 请求,Method 会是 “POST”。
    • 例子:r.Method 可以帮助你判断请求的类型。
  2. URL(请求的URL)

    • 类型:*url.URL
    • 描述:表示客户端请求的 URL 地址。这个字段是一个指向 url.URL 结构体的指针,包含了完整的 URL 信息。
    • 通过 r.URL.Path,你可以获取请求的路径(例如 /hello)。而 r.URL.Query() 可以用于获取 URL 查询参数(如 ?name=John)。
    • 例子:
      • r.URL.Path:/hello 代表访问路径。
      • r.URL.RawQuery:查询字符串部分,例如 name=John&age=30。

HTTP 响应对象 (http.ResponseWriter)

示例代码

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

import (
"net/http"
)

func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 设置响应头
w.Header().Set("Content-Type", "text/plain")
// 设置响应状态码为 201 Created
w.WriteHeader(http.StatusCreated)
// 写入响应体内容
w.Write([]byte("Hello, client!"))
})

// 启动服务器,监听端口 8080
http.ListenAndServe(":8080", nil)
}

设置响应头

代码:

1
w.Header().Set("Content-Type", "text/plain")

作用
  • 响应头(HTTP Headers)是服务器向客户端提供的关于响应的元信息。
  • Content-Type 是一个常用的响应头,它告诉客户端响应体的内容类型和格式,帮助客户端正确解析和处理数据。
  • 告知客户端数据类型:
    客户端需要知道返回的数据是什么类型,以便正确解析。例如:

    • text/plain:表示纯文本。
    • text/html:表示 HTML 文档。
    • application/json:表示 JSON 数据。

    如果没有正确设置,客户端可能会错误地解析或显示数据。

  • 支持不同的客户端

    • 浏览器、移动设备、API 客户端等都依赖 Content-Type 来决定如何处理服务器返回的数据。
    • 例如,Content-Type: text/html 会让浏览器将响应当作网页渲染,而 Content-Type: application/json 会让 API 客户端将其解析为 JSON 对象。

设置响应状态码

代码:

1
w.WriteHeader(http.StatusCreated)
作用

状态码的作用是面向客户端的

  • 客户端通过状态码判断请求结果

    • 状态码如 200 OK 表示请求成功。
    • 状态码如 404 Not Found 表示客户端请求的资源不存在。
    • 状态码如 500 Internal Server Error 表示服务器内部出了问题。
  • 客户端行为会依赖状态码

    • 浏览器看到 301 Moved Permanently 会自动重定向到新的 URL。
    • API 客户端根据 401 Unauthorized 提示用户需要登录。
    • 搜索引擎看到 503 Service Unavailable 会知道服务器暂时不可用,稍后再尝试抓取页面。

HTTP客户端

HTTP 客户端主要用于发送 HTTP 请求到指定的服务器,并接收服务器的响应。net/http 提供了一些简单易用的函数和类型,例如 http.Get、http.Post、http.Client 等。

核心类型&函数

(1) http.Client
http.Client 是一个可配置的 HTTP 客户端,用于发送请求和接收响应。
默认客户端是 http.DefaultClient。
(2) http.Request
表示一个 HTTP 请求,包含请求方法、URL、头部、请求体等。
(3) http.Response
表示一个 HTTP 响应,包含状态码、头部、响应体等。
(4) 快捷方法

  • http.Get:发送简单的 GET 请求。
  • http.Post:发送简单的 POST 请求。
  • http.Head、http.PostForm 等:发送其他类型的请求。

懒得写例子直接解释gobyexample里的

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
package main

import (
"bufio"//提供缓冲I/O操作
"fmt"
"net/http"
)

func main() {

resp, err := http.Get("http://gobyexample.com")
//发送一个http.Get到http://gobyexample.com
//返回响应对象http.Response和error
//resp是一个响应对象,包含状态码,头部和响应体
//err表示是否出错,如果不为nil则代表出错
if err != nil {
panic(err)
//若出错,则终止程序,输出错误信息
}
defer resp.Body.Close()
//保证在resp.Body使用完后立即关闭

fmt.Println("Response status:", resp.Status)//打印状态码

scanner := bufio.NewScanner(resp.Body)
//创造扫描器,逐行读取响应体中的数据

for i := 0; scanner.Scan() && i < 5; i++ {
//按行读取响应体,若有下一行则返回true
fmt.Println(scanner.Text())
}

//扫描是否有错误,有错误用panic抛出
if err := scanner.Err(); err != nil {
panic(err)
}
}

HTTP服务端

HTTP 服务端用于处理客户端的 HTTP 请求并返回响应。Go 提供了一个高效的 HTTP 服务器实现,支持路由处理和并发。

核心类型&函数

(1) http.Handler
接口,表示 HTTP 请求的处理器。
需要实现 ServeHTTP(w http.ResponseWriter, r *http.Request) 方法。
(2) http.Server
表示一个 HTTP 服务器,包含监听地址、处理器和其他配置。
默认服务器是通过 http.ListenAndServe 启动的。
(3) http.ServeMux
默认的路由器,负责将请求分发给对应的 Handler。

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
package main

import (
"fmt"
"net/http"
)

func hello(w http.ResponseWriter, req *http.Request) {

fmt.Fprintf(w, "hello\n")
}

func headers(w http.ResponseWriter, req *http.Request) {

for name, headers := range req.Header {
for _, h := range headers {
fmt.Fprintf(w, "%v: %v\n", name, h)
}
}
}

func main() {

http.HandleFunc("/hello", hello)
http.HandleFunc("/headers", headers)
//注册了两个处理器
//一个打印hello,一个打印所有请求头
http.ListenAndServe(":8090", nil)
}