golang:http.request

本文介绍了Go语言中处理HTTP请求的相关内容,包括request对象的构建、错误类型如ProtocolError的定义,以及Request结构体的详细字段。同时,文章提到了错误处理函数,如badStringError,并展示了如何修改和克隆请求的上下文。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值