1.6 获取请求参数
我们思考一下一个http请求他的请求参数(请求信息)会放到哪里?
1.请求链接的url中,例如:www.register.com/?account=12…
2.请求头中,通常为token保存的位置
3.请求体中,例如json格式的请求体,和form格式的请求体中。
那么我们就可以从这三方面下手获取请求参数。
1.6.1 url中的请求信息
func Register(ctx context.Context, c *app.RequestContext) {
acc := c.Query("account")
pwd := c.DefaultQuery("pwd", "nopasswd")
c.JSON(consts.StatusOK, utils.H{
"account": acc,
"passwd": pwd,
})
}
可以通过Query这系列函数来获取/xxx?yyy=abc&zzz=abc的参数,其中Query不存在参数时会返回空字符串,DefaultQuery第二个参数可以指定参数不存在时返回对应第二个参数的字符串。
当然如果请求参数比较固定的话,我们可以通过结构体来绑定对应的参数,需要打query标签。
type Acc struct {
Account string `query:"account"`
Passwd int `query:"passwd"`
}
func Register1(ctx context.Context, c *app.RequestContext) {
var acc Acc
c.Bind(&acc)
c.JSON(consts.StatusOK, acc)
}
Restful风格请求是我们平时最常用的请求方式。例如:
其中参数完全融入到请求连接中,例如上述请求可以理解为获取商品phone的第一页商品。这种参数我们也可以获取。方法如下: 注意注册路由信息的时候,路径信息表示方法。
r.GET("/:item/:page", handler.GetItem)
func GetItem(ctx context.Context, c *app.RequestContext) {
item := c.Param("item")
page := c.Param("page")
fmt.Println(item, page)
}
基本操作就是将路径上的参数用:标记,然后在Param方法来获取对应的参数。
同样也可以用结构体来表示参数。
type GoodsWithPage struct {
Item string `path:"item"`
Page int `path:"page"`
}
func GetItem1(ctx context.Context, c *app.RequestContext) {
var gwp GoodsWithPage
c.Bind(&gwp)
fmt.Println(gwp)
}
可以看出无论是那种url参数都可以用Bind函数来从url中将参数映射到结构体上。唯一的区别就是tag标签不同。
其中支持的tag如下:
其中vd和raw_body不常用,后面有自动生成代码来完成这两项绑定。
1.6.2 Header参数
在hz框架中使用c.GetHeader("key")
func GetHeader(ctx context.Context, c *app.RequestContext) {
token := c.GetHeader("token")
c.String(http.StatusOK, string(token))
}
设置响应头可以通过c.Response中的方法来添加响应头。
c.Response.Header.Set("acc", "ok")
1.6.3 body中的参数
body中的参数一般使用的为json格式或者postform格式。
1.PostFrom格式解析
func HandlerPostForm(ctx context.Context, c *app.RequestContext) {
acc, ok := c.GetPostForm("account")
fmt.Println("acc :", acc, ok)
c.String(http.StatusOK, acc)
}
表单的获取除了GetPostForm之外,还有PostForm和DefaultPostForm两种方法。使用方式与处理url中Query系列方法相同。
也可以使用form的tag配合结构体解析数据
type MyForm struct {
Account string `form:"account"`
}
func HandlerPostFrom2(ctx context.Context, c *app.RequestContext) {
var mf MyForm
c.Bind(&mf)
c.JSON(http.StatusOK, mf)
}
2.json格式解析
建议结构体绑定json tag获取。
type JsonReq struct {
Name string `json:"name"`
Age int `json:"age"`
}
func HandlerJson(ctx context.Context, c *app.RequestContext) {
var jsonreq JsonReq
c.Bind(&jsonreq)
fmt.Println(jsonreq)
}
1.6.4.PostForm文件传输
见文档:
无论什么参数都可以使用结构体加标签来实现请求参数映射,调用的方法是c.Bind,因为需要写操作,传入参数是结构体指针。
1.7 中间件
上面操作我们学习了hz框架如何处理请求和响应。我们可以依照现在知识我们可以完成所有的http请求。但是大多数请求处理函数会有许多重复的动作,例如:验证token操作,我们在每一个handler中都要写如对token的验证操作,显然这是不合理的。而中间件就是为了简化这部分操作。
中间件是什么?中间件就是一个函数,对应javaweb中的过滤器和拦截器。
中间件和前面写的Handler形式相同都是一个函数。其实在hz和gin框架中,我们之前学的handler都是一个中间件。
func CheckToken(ctx context.Context, c *app.RequestContext) {
token := c.GetHeader("token")
if token != nil {
fmt.Println("xxxx")
c.Abort()
} else {
fmt.Println("aaa")
c.Next(ctx)
}
}
我们可以全局注册一个中间件
func main() {
h := server.Default()
//使用use方法调全局注册中间件
h.Use(middleware.CheckToken)
register(h)
h.Spin()
}
也可以在函数注册路由时注册中间件,因为我们注册路由的方法是一个可变参数,以GET方法为例
func (group *RouterGroup) GET(relativePath string, handlers ...app.HandlerFunc)
而中间件和我们handler函数形式是一致的。我们可以向下面操作一般,在注册路由的时候注册中间件。
r.GET("/registerMiddleware",[]app.HandlerFunc{
middleware.CheckToken,
handler.Ping,
handler.XXX,
}...)
中间件原理,中间件和handler本质上都是app.HanderFunc函数。当一条请求发送到服务器时,会按照注册顺序的队列先进先调用(洋葱模型),如果我们想提前终止整个调用过程,并响应请求。只需要在中间件中使用c.xxx设置好返回值(也可以不设置),然后调用c.Abort方法提前终止调用。当然我们可以在中间件中提前调用下一个中间件方法,即使用c.Next(ctx),来在当前中间件中调用下个方法。
例如我们使用next方法,在本中间件中尚未结束时,调用下一个方法
func Middle1(ctx context.Context, c *app.RequestContext) {
fmt.Println("middle 1 start")
c.Next(ctx)
fmt.Println("middle 1 end")
}
func Middle2(ctx context.Context, c *app.RequestContext) {
fmt.Println("middle 2 start")
c.Next(ctx)
fmt.Println("middle 2 end")
}
func Middle3(ctx context.Context, c *app.RequestContext) {
fmt.Println("middle 3 start")
c.Next(ctx)
fmt.Println("middle 3 end")
}
调用结果:
提前终止中间件:
func Middle1(ctx context.Context, c *app.RequestContext) {
fmt.Println("middle 1 start")
c.Next(ctx)
fmt.Println("middle 1 end")
}
//相较于上一个在第二中间件被调用后,就停止调用后续的中间件。
func Middle2(ctx context.Context, c *app.RequestContext) {
fmt.Println("middle 2 start")
c.Abort()
fmt.Println("middle 2 end")
}
func Middle3(ctx context.Context, c *app.RequestContext) {
fmt.Println("middle 3 start")
c.Next(ctx)
fmt.Println("middle 3 end")
}
可以看出中间件被提前停止了,当调用到中间件2后并为调用中间件3。同时全局注册(h.Use注册的中间件)的中间件也生效了,打印出aaa。