golang WEB框架Hertz --- 获取参数
安装Hertz命令行工具
请确保您的Go版本在1.15及以上版本,笔者用的版本是1.18 配置好GO的环境后,按照Hertz的命名行工具
go install github.com/cloudwego/hertz/cmd/hz@latest
验证Hertz工具是否安装成功,执行下面指令
hz -v
对应的输出hertz命令行工具版本
hz version v0.2.0
新建一个Hertz项目
进入到$GOPATH下面,新建src文件夹,创建hertz_demo作为项目的根目录
cd $GOPATH
mkdir src
cd src
mkdir hertz_demo
cd hertz_demo
执行生成代码命令 hz new 或者使用hz new -module example 初始化项目,项目初始化后,hertz会自动创建对应项目文件,文件结构如下
.
├── biz
│   ├── handler
│   │   └── ping.go
│   └── router
│       └── register.go
├── go.mod
├── main.go
├── router.go
└── router_gen.go
运行Hertz项目
执行go mod tidy整理项目
启动hertz 控制台输出如下:
2022[/07/24]() 23:08:10.114348 engine.go:524: [Debug] HERTZ: Method=GET absolutePath=[/ping]() --> handlerName=hertz_demo[/biz/handler.Ping]() (num=2 handlers)
2022[/07/24]() 23:08:10.115227 transport.go:91: [Info] HERTZ: HTTP server listening on address=[::]:8888
此时访问localhost:8888/ping服务器返回
{"message":"pong"}
Hertz获取请求参数
Query参数
通过c.Query获取路径参数
func Person(ctx context.Context, c *app.RequestContext) {
	name := c.Query("name")
	c.JSON(200, utils.H{
		"data": name,
	})
}
curl http://localhost:8888/person?name=erik
输出:
{"data":"erik"}
路径参数
通过c.Param获取路径参数
func HelloPerson(ctx context.Context, c *app.RequestContext) {
   name := c.Query("name")
   age := c.Param("age")
   c.JSON(200, utils.H{
      "age":  age,
      "name": name,
   })
}
路由注册
r.GET("/person/:age", hello.HelloPerson)
请求:
curl http://localhost:8888/hello/person/12?name=erik
{
  "age": "12",
  "name": "erik"
}
获取请求Body
func PersonInfo(ctx context.Context, c *app.RequestContext) {
   type Person struct {
      Age  int    `json:"age"`
      Name string `json:"name"`
   }
   body, err := c.Body()
   if err != nil {
      panic(err)
   }
   var p Person
   if err := json.Unmarshal(body, &p); err != nil {
      panic(err)
   }
   c.JSON(200, utils.H{
      "person": p,
   })
}
curl
curl --location --request POST 'localhost:8888/person_info' \
--header 'Content-Type: application/json' \
--data-raw '{
    "age":12,
    "name":"erik"
}'
{
    "person": {
        "age": 12,
        "name": "erik"
    }
}
表单参数
func PersonForm(ctx context.Context, c *app.RequestContext) {
   age, _ := c.GetPostForm("age")
   name, _ := c.GetPostForm("name")
   c.JSON(200, utils.H{
      "age":  age,
      "name": name,
   })
}
curl --location --request POST 'localhost:8888/person_form' \
--form 'name="erik"' \
--form 'age="12"'
{
    "age": "12",
    "name": "erik"
}
文件上传&文件下载
func PersonUpload(ctx context.Context, c *app.RequestContext) {
   fileHeader, err := c.FormFile("file")
   if err != nil {
      panic(err)
   }
   open, err := fileHeader.Open()
   if err != nil {
      panic(err)
   }
   // 读取文件到字节数组
   fileRaw, err := ioutil.ReadAll(open)
   if err != nil {
      panic(err)
   }
   // 将读取到的文件写入到响应
   _, err = c.Write(fileRaw)
   if err != nil {
      panic(err)
   }
}
curl --location --request POST 'localhost:8888/person_upload' \
--form 'file=@"/img.jpeg"'
参数绑定
代码绑定是Hertz超级赞的一部分,可以非常优雅的完成请求参数映射到结构体与请求参数的验证
此处参数绑定使用了 github.com/bytedance/g…
上面分别介绍了http中常见的参数获取api,hertz出了提供api获取参数信息,还提供了参数绑定功能,帮助我们直接将请求参数绑定到结构体上并校验参数的合法性。
func PersonBind(ctx context.Context, c *app.RequestContext) {
   type person struct {
      Age  int    `path:"age" json:"age"`    // 从路径中获取参数
      Name string `query:"name" json:"name"` // 从query中获取参数
      City string `json:"city"`              // 从body中获取参数
   }
   var p person
   if err := c.BindAndValidate(&p); err != nil {
      panic(err)
   }
   c.JSON(200, utils.H{
      "person": p,
   })
}
curl
curl --location --request POST 'http://localhost:8888/person_bind/12?name=erik' \
--header 'Content-Type: application/json' \
--data-raw '{
    "city":"BeiJing"
}'
{
    "person": {
        "age": 12,
        "name": "erik",
        "city": "BeiJing"
    }
}
中间件
中间件首尾相连最终形成一个过滤器链,用户可以在中间件中设定一些通用的处理规则,比如:统一错误处理,用户信息验证,跨域处理等 Hertz提供了两个通用的中间件,一个是JWT验证,一个是Cors跨域中间件,开箱即用,详情可以参考:www.cloudwego.io/zh/docs/her…
使用跨域中间件示例
func main() {
    h := server.Default()
   // CORS for https://foo.com and https://github.com origins, allowing:
   // - PUT and PATCH methods
   // - Origin header
   // - Credentials share
   // - Preflight requests cached for 12 hours
   h.Use(cors.New(cors.Config{
      AllowOrigins:     []string{"https://foo.com"},
      AllowMethods:     []string{"PUT", "PATCH"},
      AllowHeaders:     []string{"Origin"},
      ExposeHeaders:    []string{"Content-Length"},
      AllowCredentials: true,
      AllowOriginFunc: func(origin string) bool {
         return origin == "https://github.com"
      },
      MaxAge: 12 * time.Hour,
   }))
   h.Spin()
}
错误处理
我们可以借助Hertz提供的中间件的能力,统一对错误进行处理。即在最外层的中间件捕获错误,然后根据错误类型做对应的处理。 这里需要借助三方库errors来获取go的错误堆栈,方便我们排查问题
引入errors
go get github.com/pkg/errors
hertz的app.RequestContext提供了c.Error(err)方法用于保存业务中产生的错误,c.Errors()获取业务中产生的错误。所以如果程序运行时产生错误,我们可以将错误保存到app.RequestContext中,并在中间件中获取这个错误,判断错误的类型进行对应的处理。
统一异常处理中间件代码如下:
package middleware
import (
	"context"
	"errors"
	"fmt"
	"github.com/bytedance/gopkg/util/logger"
	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/common/utils"
)
func GlobalErrorHandler(ctx context.Context, c *app.RequestContext) {
	c.Next(ctx)
	if len(c.Errors) == 0 {
		// 没有收集到异常直接返回
		fmt.Println("retun")
		return
	}
	hertzErr := c.Errors[0]
	// 获取errors包装的err
	err := hertzErr.Unwrap()
	// 打印异常堆栈
	logger.CtxErrorf(ctx, "%+v", err)
	// 获取原始err
	err = errors.Unwrap(err)
	// todo 进行错误类型判断
	c.JSON(400, utils.H{
		"code":    400,
		"message": err.Error(),
	})
}
配置中间件
package main
import (
	"hertz_demo/biz/middleware"
	"github.com/cloudwego/hertz/pkg/app/server"
)
func main() {
	h := server.Default()
	h.Use(middleware.GlobalErrorHandler)
	register(h)
	h.Spin()
}
业务代码中将错误存放到app.RequestContext中直接退出
	err = c.BindAndValidate(&req)
	if err != nil {
		fmt.Printf("%v", err.Error())
		_ = c.Error(errors.WithStack(err))
		return
	}
参数校验异常时,异常堆栈信息如下:
validating: expr_path=Name, cause=invalid
2022/07/25 23:41:47.977087 logger.go:190: [Error] [validating: expr_path=Name, cause=invalid
hertz_demo/biz/handler/person.PersonInfo
	/Users/xxx/gopath/src/hertz_demo/biz/handler/person/person_service.go:23
github.com/cloudwego/hertz/pkg/app.(*RequestContext).Next
	/Users/xxx/gopath/pkg/mod/github.com/cloudwego/hertz@v0.2.0/pkg/app/context.go:611
hertz_demo/biz/middleware.GlobalErrorHandler
	/Users/xxx/gopath/src/hertz_demo/biz/middleware/global_error_handler.go:14
github.com/cloudwego/hertz/pkg/app.(*RequestContext).Next
	/Users/xxx/gopath/pkg/mod/github.com/cloudwego/hertz@v0.2.0/pkg/app/context.go:611
github.com/cloudwego/hertz/pkg/app/middlewares/server/recovery.Recovery.func1
	/Users/xxx/gopath/pkg/mod/github.com/cloudwego/hertz@v0.2.0/pkg/app/middlewares/server/recovery/recovery.go:51
github.com/cloudwego/hertz/pkg/app.(*RequestContext).Next
	/Users/xxx/gopath/pkg/mod/github.com/cloudwego/hertz@v0.2.0/pkg/app/context.go:611
github.com/cloudwego/hertz/pkg/route.(*Engine).ServeHTTP
	/Users/xxx/gopath/pkg/mod/github.com/cloudwego/hertz@v0.2.0/pkg/route/engine.go:607
github.com/cloudwego/hertz/pkg/protocol/http1.Server.Serve
	/Users/xxx/gopath/pkg/mod/github.com/cloudwego/hertz@v0.2.0/pkg/protocol/http1/server.go:244
github.com/cloudwego/hertz/pkg/route.(*Engine).Serve
	/Users/xxx/gopath/pkg/mod/github.com/cloudwego/hertz@v0.2.0/pkg/route/engine.go:456
github.com/cloudwego/hertz/pkg/route.(*Engine).onData
	/Users/xxx/gopath/pkg/mod/github.com/cloudwego/hertz@v0.2.0/pkg/route/engine.go:353
github.com/cloudwego/hertz/pkg/network/netpoll.(*transporter).ListenAndServe.func2
	/Users/xxx/gopath/pkg/mod/github.com/cloudwego/hertz@v0.2.0/pkg/network/netpoll/transport.go:83
github.com/cloudwego/netpoll.(*connection).onRequest.func2
	/Users/xxx/gopath/pkg/mod/github.com/cloudwego/netpoll@v0.2.4/connection_onevent.go:153
github.com/cloudwego/netpoll.(*connection).onProcess.func1
	/Users/xxx/gopath/pkg/mod/github.com/cloudwego/netpoll@v0.2.4/connection_onevent.go:176
github.com/bytedance/gopkg/util/gopool.(*worker).run.func1.1
	/Users/xxx/gopath/pkg/mod/github.com/bytedance/gopkg@v0.0.0-20220623074550-9d6d3df70991/util/gopool/worker.go:69
github.com/bytedance/gopkg/util/gopool.(*worker).run.func1
	/Users/xxx/gopath/pkg/mod/github.com/bytedance/gopkg@v0.0.0-20220623074550-9d6d3df70991/util/gopool/worker.go:70
runtime.goexit
	/usr/local/go/src/runtime/asm_amd64.s:1571]
响应结果:
{
    "code": 400,
    "message": "validating: expr_path=Name, cause=invalid"
}
代码生成
上面所有的代码都可以通过Hertz提供的代码生成器生成,Hertz代码生成是通过thrift或者grpc的idl生成的。详情可以查看www.cloudwego.io/zh/docs/her… 这里推荐使用grpc生成代码
这里介绍通过GRPC生成Hertz代码的方式
安装protobuf工具
// brew 安装
brew install protobuf
// 官方镜像安装,以 macos 为例
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-osx-x86_64.zip
unzip protoc-3.19.4-osx-x86_64.zip
cp bin/protoc /usr/local/bin/protoc
// 确保 include/google 放入 /usr/local/include下
cp -r include/google /usr/local/include/google
项目根目录新建idl文件夹,添加api.proto
syntax = "proto3";
package api;
import "google/protobuf/descriptor.proto";
option go_package = "/api";
extend google.protobuf.FieldOptions {
  optional string raw_body = 50101;
  optional string query = 50102;
  optional string header = 50103;
  optional string cookie = 50104;
  optional string body = 50105;
  optional string path = 50106;
  optional string vd = 50107;
  optional string form = 50108;
  optional string go_tag = 51001;
  optional string js_conv = 50109;
}
extend google.protobuf.MethodOptions {
  optional string get = 50201;
  optional string post = 50202;
  optional string put = 50203;
  optional string delete = 50204;
  optional string patch = 50205;
  optional string options = 50206;
  optional string head = 50207;
  optional string any = 50208;
  optional string gen_path = 50301;
  optional string api_version = 50302;
  optional string tag = 50303;
  optional string name = 50304;
  optional string api_level = 50305;
  optional string serializer = 50306;
  optional string param = 50307;
  optional string baseurl = 50308;
}
extend google.protobuf.EnumValueOptions {
  optional int32 http_code = 50401;
}
新建person文件夹,新建person.proto文件
syntax = "proto3";
package person;
option go_package = "hertz/person";
import "api.proto";
message PersonReq {
  string name = 1[(api.query)="name",(api.vd)="len($)>0"];
  int32 age = 2[(api.path)="age"];
  string city = 3[(api.body)="city"];
}
message PersonResp {
  string name = 1;
  int32 age = 2;
  string city = 3;
}
service PersonService {
  rpc PersonInfo(PersonReq) returns(PersonResp) {
    option (api.post) = "/person_info/:age";
  }
}
整个idl的路径如下:
├── idl
│   ├── api.proto
│   └── person
│       └── person.proto
生成代码
hz new  -I idl -idl idl/person/person.proto
整理代码
go mod tidy
生成代码后,项目路径如下:
.
├── biz
│   ├── handler
│   │   ├── person
│   │   │   └── person_service.go
│   │   └── ping.go
│   ├── model
│   │   ├── api
│   │   │   └── api.pb.go
│   │   └── hertz
│   │       └── person
│   │           └── person.pb.go
│   └── router
│       ├── person
│       │   ├── middleware.go
│       │   └── person.go
│       └── register.go
├── go.mod
├── go.sum
├── idl
│   ├── api.proto
│   └── person
│       └── person.proto
├── main.go
├── router.go
└── router_gen.go
我们只需要在对应的person_service.go中添加业务逻辑即可
// Code generated by hertz generator.
package person
import (
	"context"
	person "hertz_demo/biz/model/hertz/person"
	"github.com/cloudwego/hertz/pkg/app"
	"github.com/pkg/errors"
)
// PersonInfo .
// @router /person_info [GET]
func PersonInfo(ctx context.Context, c *app.RequestContext) {
	var err error
	var req person.PersonReq
	err = c.BindAndValidate(&req)
	if err != nil {
		_ = c.Error(errors.WithStack(err))
		return
	}
	resp := &person.PersonResp{
		Name: req.Name,
		Age:  req.Age,
		City: req.City,
	}
	c.JSON(200, resp)
}
												
											golang WEB框架Hertz --- 获取参数的更多相关文章
- golang web框架设计6:上下文设计
		
context,翻译为上下文,为什么要设计这个结构?就是把http的请求和响应,以及参数结合在一起,便于集中处理信息,以后框架的扩展等.好多框架比如gin,都是有这个上下文结构. context结构为 ...
 - golang web框架设计3:controller设计
		
继续学习golang web框架设计 controller作用 MVC设计模式里面的这个C,控制器. Model是后台返回的数据: View是渲染页面,通常是HTML的模板页面: Controller ...
 - golang web框架设计2:自定义路由
		
继续学习谢大的Go web框架设计 HTTP路由 http路由负责将一个http的请求交到对应的函数处理(或者一个struct的方法),路由在框架中相当于一个事件处理器,而这个时间包括 用户请求的路径 ...
 - golang web框架设计7:整合框架
		
把前面写好的路由器,控制器,日志,都整合在一起 全局变量和初始化 定义一些框架的全局变量 var ( BeeApp *App AppName string AppPath string StaticD ...
 - golang web框架设计5:配置设计
		
配置信息的解析,实现的是一个key=value,键值对的一个配置文件,类似于ini的配置格式,然后解析这个文件,把解析的数据保存到map中,最后调用的时候通过几个string,int之类的函数返回相应 ...
 - golang web框架设计4:日志设计
		
beego的日志设计思路来自于seelog,根据不同的level来记录日志,beego设计的日志是一个轻量级的,采用系统log.Logger接口,默认输出到os.Stdout,用户可以实现这个接口然后 ...
 - golang web框架 kratos中的日志框架
		
kratos是bilibili开源的一个web框架. 日志用法: logger.go package kratoslog import ( "flag" "github. ...
 - golang web框架设计1:框架规划
		
GO WEB 编程13节,如何设计一个web框架 学习谢大的web框架设计 总体介绍 实现一个简易的web框架,我们采用mvc模式来进行开发. model:模型,代表数据结构.通常来说,模型类时包含查 ...
 - golang web框架 beego 学习 (三) beego获取参数
		
直接上常用的例子吧: A: 获取URL中的参数 router func init() { beego.Router("/task/?:id/?:name", &co ...
 - [Asp.Net] MVC 和Web API Action 获取参数的区别
		
Asp.net MVC 和web api 的action 在获取从前台传入的数据是有很大不同 前台使用ajax的方式向后台发起post的请求 Content-Type:application/json ...
 
随机推荐
- 链接器 ld 名称的由来
			
Linker 通常缩写为 LD,这是因为在 Unix 和类 Unix 系统中,ld 是链接器的常用命令名称.这个命名可以追溯到 Unix 系统的早期发展历史. 历史背景 Unix 早期:在 Unix ...
 - LaTeX 插入表格
			
普通表格 \begin{table}[h] % h: here \begin{center} % 一个字母代表一列 \begin{tabular}{|c|cccc|} % c: center, l: ...
 - springCloud allibaba 微服务引言
			
微服务篇: springcloud 常见组件有哪些 nacos 的服务注册表结构是怎样的 nacos 如何支撑阿里内部数十万服务注册压力 nacos 如何避免并发读写冲突问题 nacos 和eurek ...
 - FFmpeg开发笔记(五十一)适合学习研究的几个音视频开源框架
			
很多程序员想学习音视频的编程开发,却不知从何学习,因为音视频技术的体系庞大.知识杂糅,一眼望去就令人生怯.那么学习音视频建议站在前人的肩膀上,从优秀的音视频开源框架开始钻研,先熟悉这些开源工具的具体 ...
 - RxJS 系列 – Subject
			
前言 RxJS 两大概念 Observable 和 Subject. 上一篇介绍了 Observable 这篇继续接受 Subject. 参考 RxJS 建立 Observable 的基礎 - Obs ...
 - HTML / CSS – Email Marketing HTML Template
			
前言 虽然现在的 Email Client 有在进步, 但是比起 browser 还是差太远了. 假如你用 HTML5 + CSS3 的方式去写 Email Template 的话是不行的. 这篇特地 ...
 - Azure 入门系列 (第二篇 Backup 和 Disaster Recovery)
			
本系列 这个系列会介绍从 0 到 1 搭建一个 Web Application 的 Server. 间中还会带上一些真实开发常用的功能. 一共 6 篇 1. Virtual Machine (VM) ...
 - 系统编程-文件IO-fcntl系统调用
			
原型: #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ ); 功 ...
 - Android Perfetto 系列 1:Perfetto 工具简介
			
2019 年开始写 Systrace 系列,陆陆续续写了 20 多篇,从基本使用到各个模块在 Systrace 上的呈现,再到启动速度.流畅性等实战,基本上可以满足初级系统开发者和 App 开发者对于 ...
 - 深入探索Spring AI:源码分析流式回答
			
在上一章节中,我们深入分析了Spring AI的阻塞式请求与响应机制,并探讨了如何增强其记忆能力.今天,我们将重点讲解流式响应的概念与实现.毕竟,AI的流式回答功能与其交互体验密切相关,是提升用户满意 ...