避坑Go的http包
写在前面
之前在七天实现Web框架的专栏中讲到过Go内置的net/http
包启动一个服务,那篇文章没有详细解释对于http.ResponseWriter
和http.Request
两个结构体的具体使用和特别注意事项。这里算是补上那个坑。在最初的Web框架实现中,并没有考虑到这些注意事项,后续会继续将这些潜在的bug给修复。
响应ResponseWriter的坑
上图是内置http包中响应的接口定义。从中也能看出,一个HTTP响应比较重要的三个东西
- 响应头【Header】
- 响应体【Write】
- 状态码【WriteHeader】
坑1——WriterHeader
在业务逻辑中如果使用http.ResponseWriter的WriterHeader方法设置状态码,后续状态码就不能再修改了
坑2——Writer
在业务逻辑中,如果使用http.ResponseWriter的Writer方法向客户端写入数据,响应头字段就不能再修改了
坑3——Header
- 在业务逻辑中,http.ResponseWriter的Header方法设置响应头,它必须必须在首次使用Writer之前使用
- 向响应头中写入自定义的信息,最好是以X开头,并且单词与单词之间用连字符链接。
X-Auth-Token
请求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
}
坑1——Body
- 不知道大家有没有遇到这样的问题,就是我在中间件中读取完了请求体中的数据,然后在视图函数中再次读取就拿不到数据了。这就是他的坑
- 我们先看下这个Body具体是个什么东西
3. 它是一个接口来着,并且对于http.Request中的Body具体实现,是只能读一次的。所以当我们想要在上下文中缓存请求体中的数据怎么办
- 方式一:自己构建一个
io.ReadCloser
,保存请求中的Body数据,下次读取直接从这个字段中读取 - 方式二:使用
http.Request.GetBody()
方法,用这个方法缓存请求中的Body数据。在上图中,Request结构体中的最后一个属性是GetBody
,它是一个方法类型的属性。默认是nil的,但是当我们初始化,或者说给这个方法设置一个自定义的方法,它就能为我们所用了。我们的想法是:在这个方法中,将请求中的Body缓存起来。
func main(){
// 内置方法MaxBytesReader
// 第一个参数:可写的IO流
// 第二个参数:可读的IO流
// 第三个参数:最大可读字节
// MaxBytesReader
req.Body = http.MaxBytesReader(w, req.Body, maxRequestBodySize)
body1, err := ioutil.ReadAll(req.GetBody())
body2, err := ioutil.ReadAll(req.GetBody())
// 读取两次,获取到同样的数据
req.Body.Close()
}
坑2——Query
- 查询参数:
http://www.baidu.com?username=neo?password=neo123
- 这里需要注意的是,调用
Query
方法,返回来的是一个[]string
切片 - 并且所有的值都是
string
类型,如需类型转换需要自己手动实现
坑3——Header
- 一般来说,请求中的Header大体上分为两类
- HTTP预定义的
- 自定义的
- Go会自动将请求的Header中的名字转换为标准的名字——就是调整大小写
- 一般用X开头来表明自定义的字段。
X-Auth-Token
坑4——Form表单
- 使用这两个Form之前,必须先调用
ParseForm
方法,这是将请求体中的数据解析到这些字段中。
2. Form:解析URL中的查询参数、POST、PATCH、PUT的表单数据
3. PostForm:解析PATCH、POST、PUT的表单数据
转载自:https://juejin.cn/post/7224682976651165756