request 表示由服务器接收或由客户端发送的HTTP请求,例如客户端(client)在发送各种请求时,需要先新建一个请求对象,然后调用一些请求的方法开始自定义一些配置,服务端监听到该请求便会做出相应的应答
源码解析
request错误类型
const (
defaultMaxMemory = 32 << 20 // 32 MB // 默认最大内存32 MB
)
var ErrMissingFile = errors.New("http: no such file")// 提供的文件字段名不在请求中或非文件字段时,FormFile返回
// 弃用的省略(并非http包中与协议错误相关的所有错误都属于ProtocolError类型。)
type ProtocolError struct {
ErrorString string
}
func (pe *ProtocolError) Error() string { return pe.ErrorString }
var (
// Pusher实现的Push方法返回ErrNotSupported,以指示HTTP/2push支持不可用
ErrNotSupported = &ProtocolError{"feature not supported"}
// 当请求的内容类型不包含“boundary”参数时,request.MultipartReader返回
ErrMissingBoundary = &ProtocolError{"no multipart boundary param in Content-Type"}
// Content-Type不是multipart/form-data时,request.MultipartReader返回
ErrNotMultipart = &ProtocolError{"request Content-Type isn't multipart/form-data"}
)
func badStringError(what, val string) error { return fmt.Errorf("%s %q", what, val) }
- ErrMissingFile:这是请求对象的一个查找方法返回的错误,调用了errors包的New方法来返回一个错误类型的错误(即errors.New可以传string类型参数 来返回一个简单的字符串错误)
- ProtocolError :ProtocolError表示HTTP协议错误结构体对象。该对象 实现了error接口(Error() string),因此,所有该结构体实例都可以当成error返回传参
- badStringError:是一个根据两个 string参数返回一个error的函数,当必要时,可以调用此函数实现返回错误
request 结构体定义
// Headers that Request.Write 处理自身应跳过
var reqWriteExcludeHeader = map[string]bool{
"Host": true, // 反正不在Header的map中
"User-Agent": true,
"Content-Length": true,
"Transfer-Encoding": true,
"Trailer": true,
}
// Request 请求结构体:在客户机和服务器使用之间,字段语义略有不同。 除了以下字段的注释外,请参阅文档以了解Request.Write and RoundTripper
type Request struct {
// 指定发送的HTTP请求的方法(GET、POST、PUT等),Go的HTTP客户端不支持使用CONNECT方法发送请求
Method string
// URL 指定被请求的URI(对于服务器请求)或要访问的URL(对于客户端请求,就是要连接的服务器地址)
URL *url.URL
// 入服务器请求的协议版本(主版本号.次版本号)
Proto string // "HTTP/1.0"
ProtoMajor int // 1
ProtoMinor int // 0
// 请求头(为请求报文添加了一些附加信息),不区分大小写,对于客户端请求,某些头(如内容长度和连接)会在需要时自动写入
Header Header
// 请求体,get请求没有请求体-Body字段(get请求的参数都在url里)
Body io.ReadCloser
// 获取请求体的副本
GetBody func() (io.ReadCloser, error)
// 请求Body的大小(字节数)
ContentLength int64
// 列出从最外层到最内层的传输编码。空列表表示“身份”编码,通常可以忽略;在发送和接收请求时,根据需要自动添加和删除分块编码。
TransferEncoding []string
// 在回复了此次请求后结束连接。对服务端来说就是回复了这个 request ,对客户端来说就是收到了 response
// 且 对于服务端是Handlers 会自动调用Close, 对客户端,如果设置长连接(Transport.DisableKeepAlives=false),就不会关闭。没设置就关闭
Close bool
// 对于服务器请求,Host指定查找URL的主机
Host string
// Form 包含解析的表单数据,包括URL字段的查询参数和补丁、POST或PUT表单数据, 此字段仅在调用ParseForm后可用。
Form url.Values
// PostForm 包含来自PATCH、POST或PUT body参数的解析表单数据, 此字段仅在调用ParseForm后可用。HTTP客户机忽略PostForm,而是使用Body。
PostForm url.Values
// MultipartForm 是经过解析的多部件 表单,包括文件上传. 此字段仅在解析表单即 调用ParseMultipartForm后可用。 HTTP客户机忽略MultipartForm,而是使用Body。
MultipartForm *multipart.Form
// Trailer 指定在 当body部分发送完成之后 发送的附加头
// 对于服务器请求,尾部映射最初只包含尾部键,值为nil。(客户端声明它稍后将发送哪些trailers)当处理程序从主体读取时,它不能引用trailers
// 从Body读取返回EOF后,可以再次读取Trailer并包含非nil值
// 对于客户端请求,必须将拖车初始化为包含trailer键的map,以便稍后发送。
// 很少有HTTP客户机、服务器或代理支持HTTP Trailer
Trailer Header
// RemoteAddr 允许HTTP服务器和其他软件记录发送请求的网络地址,通常用于日志记录。
// 此字段不是由ReadRequest填写的,并且没有定义的格式。此包中的HTTP服务器在调用处理程序之前将RemoteAddr设置为“IP:port”地址。 HTTP客户端将忽略此字段。
RemoteAddr string
// RequestURI 是客户端发送到服务器的请求行(RFC 7230,第3.1.1节)的未修改的请求目标。通常应该改用URL字段。在HTTP客户端请求中设置此字段是错误的。
RequestURI string
// TLS 允许HTTP服务器和其他软件记录有关接收请求的TLS连接的信息。此字段不是由ReadRequest填写的。HTTP客户端将忽略此字段
TLS *tls.ConnectionState
// Cancel 是一个可选通道,它的关闭表示客户端请求应被视为已取消
// 不推荐:改为使用NewRequestWithContext设置请求的上下文
Cancel <-chan struct{}
// Response是导致创建此请求的重定向响应。此字段仅在客户端重定向期间填充
Response *Response
// 请求的上下文。(修改时,通过使用WithContext复制整个请求来修改它)
// 对于传出的客户机请求,上下文控制请求及其响应的整个生存期:获取连接、发送请求以及读取响应头和主体
ctx context.Context
}
在上面的代码中,我们定义了 http.Request类型中的公开数据成员,备注详细说明了其中的各行代码。
下面介绍 该对象的 获取内部成员ctx(上下文)的方法,2个修改上下文方法(都用到了内部自定义clone函数)
func (r *Request) Context() context.Context {
if r.ctx != nil {
return r.ctx
}
return context.Background()
}
func (r *Request) WithContext(ctx context.Context) *Request {
if ctx == nil {
panic("nil context")
}
// 1、首先,使用new(type), 返回一个 指向 Request类型的 零值 的指针
r2 := new(Request)
// 2、将*Request 赋值给 r2的指针(即将 指向请求数据的指针,赋值给新变量的指针。此时,新变量的指针也是指向原请求,即新变量r2是一个 数据和r相同的变量)
*r2 = *r
// 3、把r2的上下文修改成新上下文
r2.ctx = ctx
// 4、将 原请求的Url克隆进新请求的Url (cloneURL的本质就是上面指针修改方法, 内部还进行了user属性的克隆)
r2.URL = cloneURL(r.URL)
// 5、返回新的 请求
return r2
}
func (r *Request) Clone(ctx context.Context) *Request {
if ctx == nil {
panic("nil context")
}
r2 := new(Request)
*r2 = *r
r2.ctx = ctx
r2.URL = cloneURL(r.URL)
// 对比上面方法,这里新增其他 属性的克隆:Header、Trailer、TransferEncoding、Form、PostForm、MultipartForm
if r.Header != nil {
r2.Header = r.Header.Clone()
}
if r.Trailer != nil {
r2.Trailer = r.Trailer.Clone()
}
if s := r.TransferEncoding; s != nil {
s2 := make([]string, len(s))
copy(s2, s)
r2.TransferEncoding = s2
}
r2.Form = cloneURLValues(r.Form)
r2.PostForm = cloneURLValues(r.PostForm)
r2.MultipartForm = cloneMultipartForm(r.MultipartForm)
return r2
}
// ProtoAtLeast 报告请求中使用的HTTP协议是否至少 major.minor.(入服务器请求的协议版本)
func (r *Request) ProtoAtLeast(major, minor int) bool {
return r.ProtoMajor > major ||
r.ProtoMajor == major && r.ProtoMinor >= minor
}
- Context():获取对象内部成员,可以使用一个公开的方法封装实现
- WithContext(): 更改请求的上下文方法一,传入新的上下文,返回修改后的请求–r的浅层副本,该方法 通过新建一个变量,使变量的指针指向原 请求,然后克隆请求的URL实现。此方法很少用。要使用上下文创建新请求,请使用NewRequestWithContext