Hertz框架的基本使用|青训营笔记

576 阅读4分钟

这是我参与「第五届青训营」伴学笔记创作活动的第16天

Hertz是一个基于Golang语言的轻量级Web框架,用于快速构建高性能的Web应用程序和API服务。它的主要特点包括:

  1. 简单易用:Hertz的API设计简单直观,易于学习和使用。
  2. 高性能:Hertz基于Golang语言开发,具有卓越的性能表现。
  3. 轻量级:Hertz的核心代码非常小巧,不依赖任何第三方库。
  4. 支持路由:Hertz支持路由功能,可以通过不同的URL路径来访问不同的处理程序。
  5. 中间件:Hertz提供了中间件机制,可以方便地实现各种功能,如日志记录、鉴权、限流等。

官方文档: Hertz | CloudWeGo

0. hertz框架启动服务

import (  
   "context"  
  "github.com/cloudwego/hertz/pkg/app"   
  "github.com/cloudwego/hertz/pkg/app/server"  
  "github.com/cloudwego/hertz/pkg/common/utils"  
  "github.com/cloudwego/hertz/pkg/protocol/consts")  
  
func main() {  
   h := server.Default(server.WithHostPorts("127.0.0.1:8080"))  
  
   h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {  
      ctx.JSON(consts.StatusOK, utils.H{"message": "pong"})  
   })  
  
   h.Spin()  
}

1. hertz路由

Hertz 提供了 GETPOSTPUTDELETEANY 等方法用于注册路由

func main() {  
   h := server.Default(server.WithHostPorts("127.0.0.1:8080"))  
   //用于注册静态文件  
   h.StaticFS("/", &app.FS{Root: "./", GenerateIndexPages: true})  
   h.GET("/hello", func(c context.Context, ctx *app.RequestContext) {  
      ctx.JSON(consts.StatusOK, utils.H{"message": "hello"})  
   })  
   h.Spin()  
}

Hertz 提供了路由组( Group )的能力,用于支持路由分组的功能,同时中间件也可以注册到路由组上

func main(){
	h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
	v1 := h.Group("/v1")
	v1.GET("/get", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "get")
	})
	v1.POST("/post", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "post")
	})
	v2 := h.Group("/v2")
	v2.PUT("/put", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "put")
	})
	v2.DELETE("/delete", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "delete")
	})
	h.Spin()
}

在路由组中使用中间件

	v1 := h.Group("/v1", basic_auth.BasicAuth(map[string]string{"test": "test"}))

	v1.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		ctx.String(consts.StatusOK,"ping")
	})

参数路由

	h.GET("/hertz/:version", func(ctx context.Context, c *app.RequestContext) {
		// 获取参数路由参数
		version := c.Param("version")
		c.String(consts.StatusOK, "Hello %s", version)
	})

通配路由

	h.GET("/hertz/:version/*action", func(ctx context.Context, c *app.RequestContext) {
		version := c.Param("version")
		// 获取通配路由参数
		action := c.Param("action")
		message := version + " is " + action
		c.String(consts.StatusOK, message)
	})

2. 参数绑定与验证

基本使用示例:

func main() {
	r := server.New()

    r.GET("/hello", func(c context.Context, ctx *app.RequestContext) {
        // 参数绑定需要配合特定的go tag使用
		type Test struct {
            A string `query:"a" vd:"$!='Hertz'"`
        }

        // BindAndValidate
        var req Test
        err := ctx.BindAndValidate(&req)

        ...

	    // Bind
        req = Test{}
        err = ctx.Bind(&req)

        ...

        // Validate,需要使用 "vd" tag
        err = ctx.Validate(&req)

        ...
    })
...
}

支持的tag

  1. path:绑定 url 上的路径参数,相当于 hertz 路由{:param}或{*param}中拿到的参数。例如:如果定义的路由为: /v:version/example,可以把 path 的参数指定为路由参数:path:"version",此时,url: http://127.0.0.1:8888/v1/example,可以绑定path参数"1"
  2. form: 绑定请求的 body 内容。content-type -> multipart/form-data 或 application/x-www-form-urlencoded,绑定 form 的 key-value
  3. query: 绑定请求的 query 参数
  4. json: 绑定请求的 body 内容 content-type -> application/json,绑定 json 参数

参数绑定优先级

path > form > query > cookie > header > json > raw_body

设置默认值 参数支持 “default” tag 进行默认值的配置,使用方法如下:

// 生成的代码
type UserInfoResponse struct {
   NickName string `default:"Hertz" json:"NickName" query:"nickname"`
}

3. 中间件概述

3.1 服务端中间件

Hertz 服务端中间件是 HTTP 请求-响应周期中的一个函数,提供了一种方便的机制来检查和过滤进入应用程序的 HTTP 请求, 例如记录每个请求或者启用CORS。

中间件可以在请求更深入地传递到业务逻辑之前或之后执行:

  • 中间件可以在请求到达业务逻辑之前执行,比如执行身份认证和权限认证,当中间件只有初始化(pre-handle)相关逻辑,且没有和 real handler 在一个函数调用栈中的需求时,中间件中可以省略掉最后的.Next,如图1的中间件 B。
  • 中间件也可以在执行过业务逻辑之后执行,比如记录响应时间和从异常中恢复。如果在业务 handler 处理之后有其它处理逻辑( post-handle ),或对函数调用链(栈)有强需求,则必须显式调用.Next,如图1的中间件 C。

3.1.1 如何实现一个中间件

// 方式一
func MyMiddleware() app.HandlerFunc {
  return func(ctx context.Context, c *app.RequestContext) {
    // pre-handle
    // ...
    c.Next(ctx)
  }
}

// 方式二
func MyMiddleware() app.HandlerFunc {
  return func(ctx context.Context, c *app.RequestContext) {
    c.Next(ctx) // call the next middleware(handler)
    // post-handle
    // ...
  }
}

3.1.2 终止中间件调用

中间件会按定义的先后顺序依次执行,如果想快速终止中间件调用,可以使用以下方法,注意当前中间件仍将执行

  • Abort():终止后续调用
  • AbortWithMsg(msg string, statusCode int):终止后续调用,并设置 response中body,和状态码
  • AbortWithStatus(code int):终止后续调用,并设置状态码

3.1.3 server级别中间件

对整个server生效

h := server.Default()
h.Use(GlobalMiddleware())

3.1.4 路由组级别中间件

对当前路由组生效

h := server.Default()
group := h.Group("/group")
group.Use(GroupMiddleware())

3.1.5 单一路由级别中间件

func PathMiddleware() []app.HandlerFunc {
	return []app.HandlerFunc{func(ctx context.Context, c *app.RequestContext) {
		fmt.Println("path middleware")
		c.Next(ctx)
	}}
}

func main() {
	h := server.Default(server.WithHostPorts("127.0.0.1:8888"))

	h.GET("/path", append(PathMiddleware(),
		func(ctx context.Context, c *app.RequestContext) {
			c.String(http.StatusOK, "path")
		})...)

	h.Spin()
}