这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
这次我们来看看Hertz框架。
具体的api文档在cloudwego[基本特性 | CloudWeGo]
库的安装参照文档即可。
创建服务端
导入hertz后,我们可以使用default设置开启一个默认的服务。
h := server.Default()
此时就可以启动一个server了。但是这个server还没有配置路由,所以你访问不了他。
你可以使用WithHostPorts来绑定我们需要的ip与端口:
h := server.Default(server.WithHostPorts(":8080"))
这样我们就绑到了0.0.0.0:8080上了。
创建完成后,我们就可以添加我们的路由,随后使用Spin()方法开始监听端口了。
server默认使用的是字节自家的netpoll。你也可以使用new来创建服务端的时候指定是否使用netpoll还是go原生的网络库。
server.New(server.WithTransport(standard.NewTransporter))
server.New(server.WithTransport(netpoll.NewTransporter))
standard就是go原生,而netpoll就是默认的网络库。目前netpoll还没有支持TLS所以如果要使用TLS的话还得用原生。文档中提到了两者的不同之处。通俗来讲就是小规模的读写操作使用netpoll性能会更好,而对于大规模的读写操作,使用go原生会好些,netpoll会存在内存读写压力。
路由
路由注册
Hertz提供了GET、POST、DELETE、PUT、PATCH、HEAD、OPTIONS和ANY的路由注册,可以直接使用hertz提供的相关的方法,或者使用Handle来手动的指定。Hertz也提供了静态文件注册。
这里不考虑中间件。
r.GET("/ping", handler.Ping)
r.POST("/post", func(c context.Context, ctx *app.RequestContext) {
ctx.String(consts.StatusOK, "post")
})
其他的方法类似。
而Handle方法,可以让我们注册自定义路由,例如我们可以自定一个REPLACE方法,然后使用Handle去注册他:
r.Handle("REPLACE", "/replace", func(c context.Context, ctx *app.RequestContext) {
ctx.String(consts.StatusOK, "replace")
})
路由类型
hertz的路由分为三种,分别是静态路由、参数路由和通配路由。这三者的路由优先级也是从高到低的。如果能够匹配上高级的将会转发到相应的业务逻辑中。
静态路由就是我们的路径为静态的。上文中我们使用的"/ping"、"/post"就属于静态路由。
如果我们在url中间使用了:name类型的字符串那么其将会成为占位符,会更具具体的请求而变化:
r.GET("/get/:name", func(c context.Context, ctx *app.RequestContext) {
ctx.String(consts.StatusOK, fmt.Sprintf("hello, %s", ctx.Param("name")))
})
那么我们访问"localhost:port/get/lihua",就会返回“hello, lihua"。
我们可以在requestcontext中使用Parma来获取参数,Query方法用于获取url中的参数(?后面的等号键值对类型的参数,如:/get?name=lihua, 就可以使用Query("name")来取得lihua这个字符串)
注意,我们我们访问的url是"/get/",或者"/get/lihua/photo"这样的是不会匹配的。
最后是我们的通配路由:和参数路由类似:
r.GET("/get/:name/*item", func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(consts.StatusOK, utils.H{
"Name": ctx.Param("name"),
"item": ctx.Param("item"),
})
})
此时"/get/lihua/photo", "/get/lihua/phone/photo", "/get/lihua/"都会路由到这个业务逻辑中。
参数绑定
Hertz提供给了一个方便将请求参数序列化到结构体中的方法,需要配合tag使用。
func Test_bind(c context.Context, ctx *app.RequestContext) {
type Data struct {
Name string `path:"name" json:"name"`
Age string `query:"age" json:"age"`
Message string `json:"message"`
}
var p Data
err := ctx.BindAndValidate(&p)
if err != nil {
fmt.Printf("Bad input, %s", err)
return
}
ctx.JSON(consts.StatusOK, fmt.Sprintf("%#v", p))
}
//curl --location --request GET "http://localhost:8888/get/lihua?age=12" \
//--header "Content-Type: application/json" --data-raw '{"message": "this is test"}'
// "handler.Data{Name:\"lihua\", Age:\"12\", Message:\"this is test\"}"
path tag说明这个参数要从路径参数中来,值为路径参数的名称。你可将其理解为上文的Param和Query。 如果我们的body是json,那么我们可以将其序列化到结构体。很方便的一个功能。
具体的tag可以在文档中看到[绑定与校验 | CloudWeGo]
这里我们使用的是bindandvalidate,也可以单独使用bind和validate这两个方法。
hz脚手架
hertz提供了一个脚手架hz。可以帮助我们直接生成代码。hz的安装在文档中有所提及,跟着步骤走即可。
hz默认在GOPATH下的src目录释放代码。如果你想要在其他地方释放代码需要添加-module name参数。
hz new生成的默认代码如下结构:
.
├── biz
│ ├── handler
│ │ └── ping.go
│ └── router
│ └── register.go
├── go.mod
├── main.go
├── router.go
└── router_gen.go
main.go是我们的程序入口。在此我们创建server,并调用register注册我们的路由,随后使用spin进行监听。
我们的注册操作在router.go中。这里我们写入我们需要注册那些路由。相应的业务逻辑则是在./biz/handler中。./biz/router则是在我们使用IDL的时候会使用到。