8.02 课后复习-Context
PART1. 对Body的输入进行JSON反序列化
context.go:
package web
import (
"encoding/json"
"errors"
"net/http"
)
// Context HandleFunc的上下文
type Context struct {
// Req 请求
Req *http.Request
// Resp 响应
Resp http.ResponseWriter
// PathParams 路径参数名值对
PathParams map[string]string
}
// BindJSON 绑定请求体中的JSON数据到给定的目标对象上 这个目标对象可能是某个结构体的实例 也有可能是个map
func (c *Context) BindJSON(target any) error {
if target == nil {
return errors.New("web绑定错误: 给定的实例为空")
}
if c.Req.Body == nil {
return errors.New("web绑定错误: 请求体为空")
}
decoder := json.NewDecoder(c.Req.Body)
return decoder.Decode(target)
}这里之所以不用json.Unmarshal(),是因为用这个API多一步将io.Reader接口转换为[]byte的过程:
context.go:
PART2. 处理表单输入
无论你后续准备使用
http.Request.Form还是http.Request.PostForm,都得先调用http.ParseForm()方法解析表单数据,这俩字段才有值http.Request.FormValue()方法内部虽然会调用http.ParseForm()方法解析表单数据,但是这个方法不抛出error,所以最好事前手动调用一次我在复习时,将检测给定的key是否存在也作为了一个判断错误的条件
context.go:
PART3. 处理查询参数
3.1 基本实现
context.go:
3.2 缓存查询参数
多次调用http.Request.URL.Query()会重复解析URL,因此需要将查询参数缓存起来
注意,这里缓存参数时,不要使用map[string][]string的数据类型,虽然它和url.Values是同一种类型,因为后边的代码写的会非常难受:
context.go:
就保持和API一致,还使用url.Values即可.原因有2个:
这里将
queryValue设置成私有字段,意味着它的作用域不会离开web包,所以它和框架使用者的代码是隔离的,只能通过Context. QueryValue()方法来访问queryValue中的K-V(确切的说只能访问V)后续通过Key查找Value时,
url.Values类型是有API的(当然最终的版本并没有使用这个API),而如果使用map[string][]string,则必须自行判断:key是否存在
key对应的value是否为空slice
context.go:
这里需要注意的是,这个缓存是不存在失效和不一致的问题的.因为一旦服务端接收到一个请求,那么这个请求的参数就是固定的了
3.3 无法区分key不存在的情况与key对应的参数值是空字符串的情况
注意url.Values.Get()方法的返回值类型为string,所以即使没有找到key对应的查询参数,它依然返回一个空字符串.所以其实到底用url.Values还是map[string][]string其实区别并不大
context.go:
PART4. 处理路径参数
路径参数在查找路由树时就已经存起来了,这里只需要取就行
context.go:
PART5. 返回不同数据类型的输入
思路:定义一个结构体,该结构体用于将string类型的输入转换为不同的类型;以上所有方法不再直接返回string类型的值,而是返回一个该结构体idea实例.注意这里返回的是实例而非指针,因为这个结构体中的属性不该被修改,换言之该结构体是"只读"的.
5.1 定义ReqValue结构体
reqValue.go:
5.2 定义各种类型转换的方法
reqValue.go:
5.3 context的各个处理请求参数的方法中返回ReqValue
context.go:
5.4 测试用例
context_test.go:
Last updated