这一期我们讲讲 Go Web 中的 Request
,关于 HTTP 请求(Request)和响应(Response)消息的结构这里简单提一下,毕竟是计算机网络的知识了(以后有时间再补充计算机网络这方面的内容),它们都具有相同的结构:
- 请求(或者响应)行
- 零个或多个头部(Header)
- 一个空行
- 可选的消息体(Body)
Request 结构体
在 Go 中的 net/http 包提供了用于表示 HTTP 消息的结构,例如 HTTP 请求结构 Request
,它是一个结构体,代表了客户端发送的 HTTP 请求消息,下面是截取出来的 Request
结构体的定义:
type Request struct {
Method string
URL *url.URL
Proto string // "HTTP/1.0"
ProtoMajor int // 1
ProtoMinor int // 0
Header Header
Body io.ReadCloser
GetBody func() (io.ReadCloser, error)
ContentLength int64
TransferEncoding []string
Close bool
Host string
Form url.Values
PostForm url.Values
MultipartForm *multipart.Form
Trailer Header
RemoteAddr string
RequestURI string
TLS *tls.ConnectionState
Cancel <-chan struct{}
Response *Response
ctx context.Context
}
其中几个常用的比较重要的字段有 URL
、 Header
、 Body
、 Form
这些,当然我们也可以通过 Request
的方法访问请求中的 Cookie
、 User Agent
等信息。
Request 中的 URL
Request 中的 URL
字段就代表了请求行里面的部分内容,查看源码我们知道 URL 字段是指向 url.URL
类型的一个指针,而 url.URL
是一个结构体:
type URL struct {
Scheme string
Opaque string // encoded opaque data
User *Userinfo // username and password information
Host string // host or host:port
Path string // path (relative paths may omit leading slash)
RawPath string // encoded path hint (see EscapedPath method)
ForceQuery bool // append a query ('?') even if RawQuery is empty
RawQuery string // encoded query values, without '?'
Fragment string // fragment for references, without '#'
RawFragment string // encoded fragment hint (see EscapedFragment method)
}
相对应的 URL 通用格式为:
scheme://[userinfo@]host/path[?query][#fragment]
或者
scheme:opaque[?query][#fragment]
URL 查询字符串由 RawQuery
字段表示,其提供实际查询的字符串。例如:http://localhost:8080/post?a_id=1&b_id=2
,它的 RawQuery
的值就是 a_id=1&b_id=2
。
URL 中的 Query
方法提供查询字符串对应的 map[string][]string
。例如:
package main
import (
"log"
"net/http"
)
func main() {
server := http.Server{
Addr: "localhost:8080",
Handler: nil,
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
url := r.URL
query := url.Query()
aId := query["a_id"]
log.Println(aId)
bId := query["b_id"]
log.Println(bId)
cId := query.Get("c_id")
log.Println(cId)
})
server.ListenAndServe()
}
运行后访问 http://localhost:8080/?a_id=12&b_id=56&c_id=89
控制台会输出如下:
对于 URL 中的 Fragment
字段来说,从浏览器发出的请求,我们无法提取出 Fragment
字段的值,因为浏览器在发送请求时会把 Fragment
部分去掉,但注意,不是所有的请求都是从浏览器发出的。
Request 中的 Header
HTTP 请求和响应的 headers 是通过 Header
类型来描述的,它是一个 map
,用来表述 HTTP Header 里的 Key-Value 对。Header map 的 key 是 string
类型,value 是 []string
。设置 key 的时候会创建一个空的 []string
作为 value ,value 里面第一个元素就是新 header 的值。执行 append
操作可以为指定的 key 添加一个新的 header 值。
通过 r.Header
能获取到 map
。
例如:r.Header["Accept-Encoding"]
返回:[gzip, deflate]([]string 类型)
例如:r.Header.Get("Accept-Encoding")
返回:gzip, deflate(string 类型)
其中 r 为 *http.Request 类型。
例如:
package main
import (
"fmt"
"net/http"
)
func main() {
server := http.Server{
Addr: "localhost:8080",
}
http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, r.Header)
fmt.Fprintln(w, r.Header["Accept-Encoding"]) // [gzip, deflate, br]
fmt.Fprintln(w, r.Header.Get("Accept-Encoding")) // gzip, deflate, br
})
server.ListenAndServe()
}
使用浏览器访问 http://localhost:8080/test
会看到类似下面的输出:
map[Accept:[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9] Accept-Encoding:[gzip, deflate, br] Accept-Language:[zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6] Connection:[keep-alive] (后面省略)]
[gzip, deflate, br]
gzip, deflate, br
Request 中的 Body
请求和响应的 bodies 都是使用 Body
字段来表示的, Body
是一个 io.ReadCloser
接口:
// ReadCloser is the interface that groups the basic Read and Close methods.
type ReadCloser interface {
Reader
Closer
}
其中, ReadCloser
接口包含一个 Reader
接口和一个 Closer
接口:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
通过上面的源码,我们知道 Reader
接口定义了一个 Read
方法,其中的参数为 []byte
,返回 byte 的数量以及可选的错误。而 Closer
接口定义了一个 Close
方法,该方法没有参数,返回可选的错误。
如果想要读取请求 body 的内容,可以调用 Body 的 Read
方法。