深入理解 Hertz RequestContext 与 请求 关联特性
URI
func (ctx *RequestContext) Host() []byte
func (ctx *RequestContext) FullPath() string
func (ctx *RequestContext) SetFullPath(p string)
func (ctx *RequestContext) Path() []byte
func (ctx *RequestContext) Param(key string) string
func (ctx *RequestContext) Query(key string) string
func (ctx *RequestContext) DefaultQuery(key, defaultValue string) string
func (ctx *RequestContext) GetQuery(key string) (string, bool)
func (ctx *RequestContext) QueryArgs() *protocol.Args
func (ctx *RequestContext) URI() *protocol.URI
-
Host:获取请求的主机地址
-
FullPath:获取匹配的路由完整路径,对于未匹配的路由返回空字符串。
-
SetFullPath:设置 FullPath 的值
注意:FullPath 由路由查找时分配,通常你不需要使用 SetFullPath 去覆盖它。
-
Path:获取请求的路径。
注意:出现参数路由时 Path 给出命名参数匹配后的路径,而 FullPath 给出原始路径。
-
Param:获取路由参数的值。
-
Query:获取路由
Query String参数中指定属性的值,如果没有返回空字符串。 -
DefaultQuery:获取路由
Query String参数中指定属性的值,如果没有返回设置的默认值。 -
GetQuery:获取路由
Query String参数中指定属性的值以及属性是否存在。 -
QueryArgs:获取路由
Query String参数对象。 -
URI:返回请求的
URI对象
例子
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/hlog"
)
func main() {
h := server.Default()
h.GET(
"/hertz/:name", func(ctx context.Context, c *app.RequestContext) {
hlog.Info(string(c.Host())) // 127.0.0.1:8888
hlog.Info(string(c.Path())) // /hertz/liming
hlog.Info(c.FullPath()) // /hertz/:name
c.SetFullPath("/v1/hertz/:name")
hlog.Info(string(c.Path())) // /hertz/liming
hlog.Info(c.FullPath()) // /v1/hertz/:name
hlog.Info(c.Param("name")) // liming
hlog.Info(c.Query("key1")) // value1
hlog.Info(c.DefaultQuery("key2", "defaultValue"))
if v, ok := c.GetQuery("key3"); ok {
hlog.Info(v)
}
c.String(200, "Hello, World!")
},
)
routeInfo := h.Routes()
hlog.Info(routeInfo)
h.Spin()
}
分别访问
- 127.0.0.1:8888/hertz/liming?key1=value1
- 127.0.0.1:8888/hertz/liming?key1=value1&key2=value2&key3=vlue3
Args 对象
通过QueryArgs方法获取一个Args 对象
签名是 func (ctx *RequestContext) QueryArgs() *protocol.Args
Args 对象提供了以下方法获取/设置 Query String 参数。
| 函数签名 | 说明 |
|---|---|
func (a *Args) Set(key, value string) |
设置 Args 对象 key 的值 |
func (a *Args) Reset() |
重置 Args 对象 |
func (a *Args) CopyTo(dst *Args) |
将 Args 对象拷贝到 dst |
func (a *Args) Del(key string) |
删除 Args 对象 key 的键值对 |
func (a *Args) DelBytes(key []byte) |
删除 Args 对象字节数组类型 key 的键值对 |
func (a *Args) Has(key string) bool |
获取 Args 对象是否存在 key 的键值对 |
func (a *Args) String() string |
将 Args 对象转换为字符串类型的 Query String |
func (a *Args) QueryString() []byte |
将 Args 对象转换为字节数组类型的 Query String |
func (a *Args) ParseBytes(b []byte) |
解析字节数组并将键值对存入 Args 对象 |
func (a *Args) Peek(key string) []byte |
获取 Args 对象 key 的值 |
func (a *Args) PeekExists(key string) (string, bool) |
获取 Args 对象 key 的值以及是否存在 |
func (a *Args) PeekAll(key string) [][]byte |
获取 Args 对象 key 的所有值 |
func (a *Args) Len() int |
获取 Args 对象键值对数量 |
func (a *Args) AppendBytes(dst []byte) []byte |
将 Args 对象 Query String 附加到 dst 中并返回 |
func (a *Args) VisitAll(f func(key, value []byte)) |
遍历 Args 对象所有的键值对 |
func (a *Args) WriteTo(w io.Writer) (int64, error) |
将 Args 对象 Query String 写入 io.Writer 中 |
func (a *Args) Add(key, value string) |
添加 Args 对象键为 key 的值 |
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/hlog"
"github.com/cloudwego/hertz/pkg/protocol"
)
func main() {
h := server.Default()
// GET http://127.0.0.1:8888/user?name=bar&age=&pets=dog&pets=cat
h.GET(
"/user", func(ctx context.Context, c *app.RequestContext) {
args := c.QueryArgs()
// 从args获取信息
hlog.Info(args.String()) // "name=bar&age=&pets=dog&pets=cat"
hlog.Info(string(args.QueryString())) // []byte("name=bar&age=&pets=dog&pets=cat")
hlog.Info(string(args.AppendBytes([]byte(nil)))) // []byte("name=bar&age=&pets=dog&pets=cat")
hlog.Info(string(args.Peek("name"))) // []byte("bar")
hlog.Info(args.Has("name")) // true
if age, hasAge := args.PeekExists("age"); hasAge {
hlog.Info(string(age), hasAge) // age == "", hasAge == true
}
hlog.Info(args.Len()) // 4
args.VisitAll(
func(key, value []byte) {
// 自动遍历
// 1. key == []byte("name"), value == []byte("bar")
// 2. key == []byte("age"), value == nil
// 3. key == []byte("pets"), value == []byte("dog")
// 4. key == []byte("pets"), value == []byte("cat")
hlog.Info(string(key), "==>", string(value))
},
)
pets := args.PeekAll("pets") // pets == [][]byte{[]byte("dog"), []byte("cat")}
for _, pet := range pets {
hlog.Info(string(pet)) // dog, cat
}
// 将信息发送给io.Writer
req := protocol.AcquireRequest()
_, err := args.WriteTo(req.BodyWriter())
if err != nil {
hlog.Error(err)
}
// n == 31 err == nil
hlog.Info(req.BodyBuffer().String()) // "name=bar&age=&pets=dog&pets=cat"
// 更改args
var newArgs protocol.Args
args.CopyTo(&newArgs)
newArgs.Set("version", "v1")
hlog.Warn(string(newArgs.Peek("version"))) // []byte("v1")
newArgs.Del("age")
hlog.Warn(newArgs.Has("age")) // false
newArgs.DelBytes([]byte("name"))
hlog.Warn(newArgs.Has("name")) // false
newArgs.Add("name", "foo")
hlog.Warn(string(newArgs.Peek("name"))) // []byte("foo")
newArgs.Reset()
hlog.Warn("|" + newArgs.String() + "|") // ""
// 解析 args
var newArgs2 protocol.Args
newArgs2.ParseBytes([]byte("name=bar&age=20"))
hlog.Warn(newArgs2.String()) // "name=bar&age=20"
},
)
h.Spin()
}
Get访问 http://127.0.0.1:8888/user?name=bar&age=&pets=dog&pets=cat
URI对象
通过URI方法获取一个Args 对象
签名是 func (ctx *RequestContext) URI() *protocol.URI
URI 对象提供了以下方法获取/设置 URI
| 函数签名 | 说明 |
|---|---|
func (u *URI) CopyTo(dst *URI) |
拷贝 URI 对象的副本到 dst |
func (u *URI) QueryArgs() *Args |
获取 Args 对象 |
func (u *URI) Hash() []byte |
获取 Hash 值,比如 http://example.com/user?baz=123#qwe 的 Hash 是 qwe |
func (u *URI) SetHash(hash string) |
设置 Hash |
func (u *URI) SetHashBytes(hash []byte) |
设置 []byte 类型 Hash |
func (u *URI) Username() []byte |
获取 Username |
func (u *URI) SetUsername(username string) |
设置 Username |
func (u *URI) SetUsernameBytes(username []byte) |
设置 []byte 类型 Username |
func (u *URI) Password() []byte |
获取 Password |
func (u *URI) SetPassword(password string) |
设置 Password |
func (u *URI) SetPasswordBytes(password []byte) |
设置 []byte 类型 Password |
func (u *URI) QueryString() []byte |
获取 Query String,比如 http://example.com/user?baz=123 的 Query String 是 baz=123 |
func (u *URI) SetQueryString(queryString string) |
设置 Query String,注意,在该方法之后使用 RequestHeader.SetRequestURI 可能会覆盖掉原来想设置的值 |
func (u *URI) SetQueryStringBytes(queryString []byte) |
设置 []byte 类型的 Query String,注意,在该方法之后使用 RequestHeader.SetRequestURI 可能会覆盖掉原来想设置的值 |
func (u *URI) Path() []byte |
获取 Path,比如 [http://example.com/user/he rtz](http://example.com/user/he rtz) 的 Path 是 /user/he rtz |
func (u *URI) PathOriginal() []byte |
获取未转义的 Path,比如 [http://example.com/user/he rtz](http://example.com/user/he rtz) 的 Path 是 /user/he%20rtz |
func (u *URI) SetPath(path string) |
设置 Path |
func (u *URI) SetPathBytes(path []byte) |
设置 []byte 类型 Path |
func (u *URI) String() string |
获取完整 URI 比如 http://example.com/user?baz=123 的完整 URI 是 http://example.com/user?baz=123 |
func (u *URI) FullURI() []byte |
获取 []byte 类型的完整 URI |
func (u *URI) Scheme() []byte |
获取协议,如 http |
func (u *URI) SetScheme(scheme string) |
设置协议 |
func (u *URI) SetSchemeBytes(scheme []byte) |
设置 []byte 类型的协议 |
func (u *URI) Host() []byte |
获取 Host,比如 http://example.com/user 的 Host 是 example.com |
func (u *URI) SetHost(host string) |
设置 Host |
func (u *URI) SetHostBytes(host []byte) |
设置 []byte 类型 Host |
func (u *URI) LastPathSegment() []byte |
获取 Path 的最后一部分,比如 Path /foo/bar/baz.html 的最后一部分是 baz.html |
func (u *URI) Update(newURI string) |
更新 URI |
func (u *URI) UpdateBytes(newURI []byte) |
更新 []byte 类型的 URI |
func (u *URI) Parse(host, uri []byte) |
初始化 URI |
func (u *URI) AppendBytes(dst []byte) []byte |
将完整的 URI 赋值到 dst 中并返回 dst |
func (u *URI) RequestURI() []byte |
获取 RequestURI,比如 http://example.com/user?baz=123 的 RequestURI 是 /user?baz=123 |
func (u *URI) Reset() |
重置 URI |
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/hlog"
"github.com/cloudwego/hertz/pkg/protocol"
)
func main() {
h := server.Default()
// GET http://127.0.0.1:8888/user?name=bar&age=aaa
h.GET(
"/user", func(ctx context.Context, c *app.RequestContext) {
u := c.URI()
// CopyTo: 将当前URI对象复制到另一个URI对象
dst := &protocol.URI{}
u.CopyTo(dst)
hlog.Info("初始化前:", dst.String()) // 初始化前:http://127.0.0.1:8888/user?name=bar&age=aaa
// 使用Parse初始化URI对象,这里我们用一个简单的URL进行初始化
dst.Parse([]byte("example.com"), []byte("/user?baz=123#qwe"))
hlog.Info("初始化后:", dst.String()) // 初始化后:http://example.com/user?baz=123#qwe
// QueryArgs: 获取Args对象,用于查询参数操作(这里不详细展开Args对象的操作)
args := u.QueryArgs()
hlog.Info(args.Len()) // 2
args.VisitAll(
func(key, value []byte) {
hlog.Infof("key: %s ==> value: %s", key, value)
// key: name ==> value: bar
// key: age ==> value: aaa
},
)
// SetHash: 设置新的Hash值
u.SetHash("newhash")
// Hash: 获取Hash值
hlog.Info("Hash:", string(u.Hash())) // Hash: newhash
// SetHashBytes: 以[]byte形式设置Hash值
u.SetHashBytes([]byte("newhashbytes"))
// Username 和 Password: 获取和设置用户名和密码
u.SetUsername("myusername")
hlog.Info("Username:", string(u.Username())) // Username: myusername
u.SetPassword("mypassword")
hlog.Info("Password:", string(u.Password())) // Password: mypassword
// SetUsernameBytes 和 SetPasswordBytes: 以[]byte形式设置用户名和密码
u.SetUsernameBytes([]byte("myusernamebytes"))
u.SetPasswordBytes([]byte("mypasswordbytes"))
// QueryString: 获取查询字符串
hlog.Info("QueryString:", string(u.QueryString())) // QueryString: name=bar&age=aaa
// SetQueryString 和 SetQueryStringBytes: 设置查询字符串
u.SetQueryString("new=query&string")
u.SetQueryStringBytes([]byte("new=querybytes&string"))
// Path 和 PathOriginal: 获取路径和原始未转义的路径
hlog.Info("Path:", string(u.Path())) // Path: /user
hlog.Info("PathOriginal:", string(u.PathOriginal())) // PathOriginal: /user
// SetPath 和 SetPathBytes: 设置路径
u.SetPath("/new/path")
u.SetPathBytes([]byte("/new/pathbytes"))
// String 和 FullURI: 获取完整URI
hlog.Info("String:", u.String()) // String:http://127.0.0.1:8888/new/pathbytes?name=bar&age=aaa#newhashbytes
hlog.Info(
"FullURI:",
string(u.FullURI()),
) // FullURI:http://127.0.0.1:8888/new/pathbytes?name=bar&age=aaa#newhashbytes
// Scheme: 获取协议
hlog.Info("Scheme:", string(u.Scheme())) // Scheme:http
// SetScheme 和 SetSchemeBytes: 设置协议
u.SetScheme("https")
u.SetSchemeBytes([]byte("httpsbytes"))
// Host: 获取主机名
hlog.Info("Host:", string(u.Host())) // Host:127.0.0.1:8888
hlog.Info(u.String()) // httpsbytes://127.0.0.1:8888/new/pathbytes?name=bar&age=aaa#newhashbytes
// SetHost 和 SetHostBytes: 设置主机名
u.SetHost("new.example.com")
u.SetHostBytes([]byte("new.example.combytes"))
// LastPathSegment: 获取路径的最后一部分
hlog.Info("LastPathSegment:", string(u.LastPathSegment())) // LastPathSegment:pathbytes
hlog.Info(u.String()) // httpsbytes://new.example.combytes/new/pathbytes?name=bar&age=aaa#newhashbytes
// Update 和 UpdateBytes: 更新整个URI
u.Update("http://updated.uri/newpath?query=newvalue#newhash")
u.UpdateBytes([]byte("http://updated.uri/newpathbytes?query=newvaluebytes#newhashbytes"))
// 将完整的 URI 赋值到 dst 中并返回 dst
appendedURI := u.AppendBytes([]byte{})
hlog.Info(
"Appended URI:",
string(appendedURI),
) // Appended URI:http://updated.uri/newpathbytes?query=newvaluebytes#newhashbytes
// Reset: 重置URI对象
u.Reset()
hlog.Info(u.String()) // http:///
},
)
h.Spin()
}
Header
// RequestHeader 相关方法
// Add: 向请求头中添加键值对(不覆盖)
func (h *RequestHeader) Add(key, value string)
// Set: 设置请求头的键值对(覆盖)只覆盖第一个
func (h *RequestHeader) Set(key, value string)
// Header: 返回请求头的原始字节数据
func (h *RequestHeader) Header() []byte
// String: 返回请求头的字符串表示
func (h *RequestHeader) String() string
// VisitAll: 遍历请求头中的所有键值对
func (h *RequestHeader) VisitAll(f func(key, value []byte))
// RequestContext 相关方法
// IsGet: 判断是否为 GET 请求
func (ctx *RequestContext) IsGet() bool
// IsHead: 判断是否为 HEAD 请求
func (ctx *RequestContext) IsHead() bool
// IsPost: 判断是否为 POST 请求
func (ctx *RequestContext) IsPost() bool
// Method: 返回请求的 HTTP 方法
func (ctx *RequestContext) Method() []byte
// ContentType: 返回请求的 Content-Type 头
func (ctx *RequestContext) ContentType() []byte
// IfModifiedSince: 检查 If-Modified-Since 是否早于指定时间
func (ctx *RequestContext) IfModifiedSince(lastModified time.Time) bool
// Cookie: 返回指定键的 Cookie 值
func (ctx *RequestContext) Cookie(key string) []byte
// UserAgent: 返回请求的 User-Agent 头
func (ctx *RequestContext) UserAgent() []byte
// GetHeader: 返回指定键的请求头值
func (ctx *RequestContext) GetHeader(key string) []byte
-
Add:添加或设置键为 key 的 Header
注意:
Add 通常用于为同一个 Key 设置多个 Header,若要为同一个 Key 设置单个 Header 请使用 Set。
当作用于 Content-Type, Content-Length, Connection, Cookie, Transfer-Encoding, Host, User-Agent 这些 Header 时,使用多个 Add 会覆盖掉旧值。
-
Set:设置 Header 键值 只覆盖第一个值,后面有多的值,不会动
注意:Set 通常用于为同一个 Key 设置单个 Header,若要为同一个 Key 设置多个 Header 请使用 Add。
-
Header:获取
[]byte类型的完整的 Header -
String:获取完整的 Header
-
VisitAll:遍历所有 Header 的键值并执行 f 函数
-
Method:获取请求方法的类型
-
ContentType:获取请求头
Content-Type的值 -
IfModifiedSince:判断时间是否超过请求头
If-Modified-Since的值。注意:如果请求头不包含 If-Modified-Since 也返回 true。
-
Cookie:获取请求头
Cookie中 key 的值。 -
UserAgent:获取请求头
User-Agent的值 -
GetHeader:获取请求头中 key 的值
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/hlog"
)
func main() {
h := server.Default()
// GET http://127.0.0.1:8888/user
h.GET(
"/user", func(ctx context.Context, c *app.RequestContext) {
// 不覆盖的添加头
c.Request.Header.Add("hertz1", "value1")
c.Request.Header.Add("hertz1", "value2")
c.Request.Header.SetContentTypeBytes([]byte("application/x-www-form-urlencoded"))
// 获取所有hertz1的值
hertz1 := c.Request.Header.GetAll("hertz1")
hlog.Debugf("hertz1: %#v", hertz1) // hertz1: []string{"value1", "value2"}
// 覆盖的添加头 只覆盖第一个
c.Request.Header.Set("hertz1", "value3")
hertz1 = c.Request.Header.GetAll("hertz1")
hlog.Debugf("hertz1: %#v", hertz1) // hertz1: []string{"value3", "value2"}
c.Request.Header.Set("hertz1", "value4")
hertz1 = c.Request.Header.GetAll("hertz1")
hlog.Debugf("hertz1: %#v", hertz1) // hertz1: []string{"value4", "value2"}
// 获取所有header
hs := c.Request.Header.Header()
hlog.Debugf("所有的header: %s", string(hs))
// 所有的header: []byte(" GET /user HTTP/1.1
// Host: 127.0.0.1:8888
// Content-Type: application/x-www-form-urlencoded
// Authorization: Basic am9objoxMjM0NTY=
// key1=value; key2=value
// Accept: */*
// Postman-Token: fe9527ee-d0a0-4dc0-ad23-966a2aae3014
// Accept-Encoding: gzip, deflate, br
// Connection: keep-alive
// Hertz1: value4
// Hertz1: value2")
hlog.Debug(c.Request.Header.String())
// GET /user HTTP/1.1
// Host: 127.0.0.1:8888
// Content-Type: application/x-www-form-urlencoded
// Authorization: Basic am9objoxMjM0NTY=
// key1=value; key2=value
// Accept: */*
// Postman-Token: fe9527ee-d0a0-4dc0-ad23-966a2aae3014
// Accept-Encoding: gzip, deflate, br
// Connection: keep-alive
// Hertz1: value4
// Hertz1: value2
// 遍历所有header
c.Request.Header.VisitAll(
func(key, value []byte) {
hlog.Debugf("%s ===> %s", string(key), string(value))
// Host ===> 127.0.0.1:8888
// Content-Type ===> application/x-www-form-urlencoded
// User-Agent ===> PostmanRuntime/7.26.10
// Authorization ===> Basic am9objoxMjM0NTY=
// Cookie ===> key1=value; key2=value
// Accept ===> */*
// Postman-Token ===> 770742e2-7aa2-4405-80a5-10ca77d3527e
// Accept-Encoding ===> gzip, deflate, br
// Connection ===> keep-alive
// Hertz1 ===> value4
// Hertz1 ===> value2
},
)
// 设置ContentType
c.Request.Header.SetContentTypeBytes([]byte("application/x-www-form-urlencoded"))
hlog.Debugf("ContentType为:%s", string(c.ContentType())) // ContentType为:application/x-www-form-urlencoded
c.Request.Header.Add("Content-Type", "text/plain")
hlog.Debugf("ContentType为:%s", string(c.ContentType())) // ContentType为:text/plain
// RequestContext相关
hlog.Debugf("是否为Get请求:%t", c.IsGet())
hlog.Debugf("是否为head请求:%t", c.IsHead())
hlog.Debugf("是否为POST请求:%t", c.IsPost())
hlog.Debugf("请求方法为:%s", string(c.Method())) // 请求方法为:GET
hlog.Debugf("Cookie key1 值为:%s", string(c.Cookie("key1")))
hlog.Debugf("Cookie key2 值为:%s", string(c.Cookie("key2")))
// Cookie key1 值为:value
// Cookie key2 值为:value
hlog.Debugf("UserAgent为:%s", string(c.UserAgent()))
// UserAgent为:PostmanRuntime/7.26.10
hlog.Debugf("请求头中 Connection 的值为:%s", string(c.GetHeader("Connection")))
// 请求头中 Connection 的值为:keep-alive
},
)
h.Spin()
}
RequestHeader 对象
使用 RequestContext.Request.Header 获取 RequestHeader 对象,该对象提供了以下方法获取/设置请求头部。
| 函数签名 | 说明 |
|---|---|
func (h *RequestHeader) Method() []byte |
获取 Method |
func (h *RequestHeader) SetMethod(method string) |
设置 Method |
func (h *RequestHeader) SetMethodBytes(method []byte) |
设置 []byte 类型的 Method |
func (h *RequestHeader) IsGet() bool |
判断 Method 是否是 GET |
func (h *RequestHeader) IsHead() bool |
判断 Method 是否是 HEAD |
func (h *RequestHeader) IsPost() bool |
判断 Method 是否是 POST |
func (h *RequestHeader) IsPut() bool |
判断 Method 是否是 PUT |
func (h *RequestHeader) IsDelete() bool |
判断 Method 是否是 DELETE |
func (h *RequestHeader) IsConnect() bool |
判断 Method 是否是 CONNECT |
func (h *RequestHeader) IsOptions() bool |
判断 Method 是否是 OPTIONS |
func (h *RequestHeader) IsTrace() bool |
判断 Method 是否是 TRACE |
func (h *RequestHeader) IgnoreBody() bool |
判断是否忽略 Body (Method GET/HEAD 忽略 Body) |
func (h *RequestHeader) RequestURI() []byte |
获取 RequestURI |
func (h *RequestHeader) SetRequestURI(requestURI string) |
设置 RequestURI |
func (h *RequestHeader) SetRequestURIBytes(requestURI []byte) |
设置 []byte 类型的 RequestURI |
func (h *RequestHeader) SetProtocol(p string) |
设置协议类型,比如 HTTP/1.0 |
func (h *RequestHeader) GetProtocol() string |
获取协议类型,比如 HTTP/1.1 |
func (h *RequestHeader) IsHTTP11() bool |
判断是否是 HTTP/1.1 |
func (h *RequestHeader) SetNoHTTP11(b bool) |
设置是否不是 HTTP/1.1 |
func (h *RequestHeader) Host() []byte |
获取 Host |
func (h *RequestHeader) SetHost(host string) |
设置 Host |
func (h *RequestHeader) SetHostBytes(host []byte) |
设置 []byte 类型的 Host |
func (h *RequestHeader) ContentLength() int |
获取 Content-Length |
func (h *RequestHeader) ContentLengthBytes() []byte |
获取 []byte 类型的 Content-Length |
func (h *RequestHeader) SetContentLength(contentLength int) |
设置 Content-Length |
func (h *RequestHeader) SetContentLengthBytes(contentLength []byte) |
设置 []byte 类型的 Content-Length |
func (h *RequestHeader) InitContentLengthWithValue(contentLength int) |
初始化 Content-Length |
func (h *RequestHeader) ContentType() []byte |
获取 Content-Type |
func (h *RequestHeader) SetContentTypeBytes(contentType []byte) |
设置 Content-Type |
func (h *RequestHeader) SetNoDefaultContentType(b bool) |
控制未指定 Content-Type 时的默认发送行为,false 发送默认 Content-Type 的值,true 不发送 Content-Type |
func (h *RequestHeader) UserAgent() []byte |
获取 User-Agent |
func (h *RequestHeader) SetUserAgentBytes(userAgent []byte) |
设置 User-Agent |
func (h *RequestHeader) ConnectionClose() bool |
判断是否包含 Connection: close |
func (h *RequestHeader) SetConnectionClose(close bool) |
设置 connectionClose 标志 |
func (h *RequestHeader) ResetConnectionClose() |
重置 connectionClose 标志为 false 并删除 Connection Header |
func (h *RequestHeader) SetByteRange(startPos, endPos int) |
设置 Range (Range: bytes=startPos-endPos) |
func (h *RequestHeader) SetMultipartFormBoundary(boundary string) |
当 Content-Type=multipart/form-data 时为其设置 boundary |
func (h *RequestHeader) MultipartFormBoundary() []byte |
获取 boundary 的值 |
func (h *RequestHeader) Trailer() *Trailer |
获取 Trailer |
func (h *RequestHeader) Cookie(key string) []byte |
获取 Cookie 键为 key 的值 |
func (h *RequestHeader) SetCookie(key, value string) |
设置 Cookie 的键值 |
func (h *RequestHeader) DelCookie(key string) |
删除键为 key 的 Cookie |
func (h *RequestHeader) DelAllCookies() |
删除所有 Cookie |
func (h *RequestHeader) FullCookie() []byte |
获取所有 Cookie |
func (h *RequestHeader) Cookies() []*Cookie |
获取所有 Cookie 对象 |
func (h *RequestHeader) VisitAllCookie(f func(key, value []byte)) |
遍历所有 Cookie 的键值并执行 f 函数 |
func (h *RequestHeader) Peek(key string) []byte |
获取 []byte 类型的键为 key 的值 |
func (h *RequestHeader) Get(key string) string |
获取键为 key 的值 |
func (h *RequestHeader) PeekArgBytes(key []byte) []byte |
获取键为 key 的值 |
func (h *RequestHeader) PeekAll(key string) [][]byte |
获取 []byte 类型的键为 key 的所有值(用于获取存在相同 key 的多个值) |
func (h *RequestHeader) GetAll(key string) []string |
获取键为 key 的所有值 |
func (h *RequestHeader) PeekIfModifiedSinceBytes() []byte |
获取 If-Modified-Since |
func (h *RequestHeader) PeekContentEncoding() []byte |
获取 Content-Encoding |
func (h *RequestHeader) PeekRange() []byte |
获取 Range |
func (h *RequestHeader) HasAcceptEncodingBytes(acceptEncoding []byte) bool |
判断是否存在 Accept-Encoding 以及 Accept-Encoding 是否包含 acceptEncoding |
func (h *RequestHeader) RawHeaders() []byte |
获取原始 Header |
func (h *RequestHeader) SetRawHeaders(r []byte) |
设置原始 Header |
func (h *RequestHeader) Add(key, value string) |
添加或设置键为 key 的 Header,用于为同一个 Key 设置多个 Header,但 key 会覆盖以下 Header: Content-Type, Content-Length, Connection, Cookie, Transfer-Encoding, Host, User-Agent |
func (h *RequestHeader) InitBufValue(size int) |
初始化缓冲区大小 |
func (h *RequestHeader) GetBufValue() []byte |
获取缓冲区的值 |
func (h *RequestHeader) SetCanonical(key, value []byte) |
设置 Header 键值,假设该键是规范形式 |
func (h *RequestHeader) Set(key, value string) |
设置 Header 键值,用于为同一个 Key 设置单个 Header |
func (h *RequestHeader) SetBytesKV(key, value []byte) |
设置 []byte 类型的 Header 键值,用于为同一个 Key 设置单个 Header |
func (h *RequestHeader) DelBytes(key []byte) |
删除 Header 中键为 key 的键值对 |
func (h *RequestHeader) AddArgBytes(key, value []byte, noValue bool) |
添加 Header 键值(与 Add 不同,key 一定不会被规范化且 key 为 Content-Type, Content-Length, Connection, Cookie, Transfer-Encoding, Host, User-Agent 时不会做特殊处理) |
func (h *RequestHeader) SetArgBytes(key, value []byte, noValue bool) |
设置 Header 键值(与 Set 不同,key 一定不会被规范化且 key 为 Content-Type, Content-Length, Connection, Cookie, Transfer-Encoding, Host, User-Agent 时不会做特殊处理) |
func (h *RequestHeader) AppendBytes(dst []byte) []byte |
将完整的 Header 附加到 dst 中并返回 |
func (h *RequestHeader) Header() []byte |
获取 []byte 类型的完整的 Header |
func (h *RequestHeader) String() string |
获取完整的 Header |
func (h *RequestHeader) CopyTo(dst *RequestHeader) |
获取 RequestHeader 的副本 |
func (h *RequestHeader) VisitAll(f func(key, value []byte)) |
遍历所有 Header 的键值并执行 f 函数 |
func (h *RequestHeader) VisitAllCustomHeader(f func(key, value []byte)) |
遍历所有 Header 的键值并执行 f 函数,以下 key 除外:Content-Type, Content-Length, Cookie, Host, User-Agent) |
func (h *RequestHeader) Len() int |
返回 Header 的数量 |
func (h *RequestHeader) DisableNormalizing() |
禁用 Header 名字的规范化 (首字母和破折号后第一个字母大写) |
func (h *RequestHeader) IsDisableNormalizing() bool |
是否禁用 Header 名字的规范化,默认不禁用 |
func (h *RequestHeader) ResetSkipNormalize() |
重置 Headers,除了 disableNormalizing 状态 |
func (h *RequestHeader) Reset() |
重置 Headers |
Body
// 获取请求的原始数据(字节形式),通常用于处理非文本类型的请求体,比如文件上传。
func (ctx *RequestContext) GetRawData() []byte
// 获取请求体的内容(字节形式),如果读取过程中出错,会返回错误信息。
func (ctx *RequestContext) Body() ([]byte, error)
// 获取请求体的流式读取器(io.Reader),适用于处理大文件或流式数据。
func (ctx *RequestContext) RequestBodyStream() io.Reader
// 解析并返回 multipart/form-data 类型的表单数据,通常用于文件上传。
func (ctx *RequestContext) MultipartForm() (*multipart.Form, error)
// 获取表单中指定 key 的值(字符串形式),如果 key 不存在,返回空字符串。
func (ctx *RequestContext) PostForm(key string) string
// 获取表单中指定 key 的值(字符串形式),如果 key 不存在,返回默认值 defaultValue。
func (ctx *RequestContext) DefaultPostForm(key, defaultValue string) string
// 获取表单中指定 key 的值(字符串形式),并返回一个布尔值表示该 key 是否存在。
func (ctx *RequestContext) GetPostForm(key string) (string, bool)
// 获取 POST 请求的表单参数,返回一个包含所有参数的 Args 对象。
func (ctx *RequestContext) PostArgs() *protocol.Args
// 获取表单中指定 key 的值(字节形式),适用于处理二进制数据。
func (ctx *RequestContext) FormValue(key string) []byte
// 设置一个自定义函数,用于从请求中提取表单值。可以用于扩展或自定义表单值的解析逻辑。
func (ctx *RequestContext) SetFormValueFunc(f FormValueFunc)
Body
获取请求的 body 数据,如果发生错误返回 error。
函数签名:
func (ctx *RequestContext) Body() ([]byte, error)
示例:
// POST http://example.com/pet
// Content-Type: application/json
// {"pet":"cat"}
h.Post("/pet", func(ctx context.Context, c *app.RequestContext) {
data, err := c.Body() // data == []byte("{\"pet\":\"cat\"}") , err == nil
})
RequestBodyStream
获取请求的 BodyStream。
函数签名:
func (ctx *RequestContext) RequestBodyStream() io.Reader
示例:
// POST http://example.com/user
// Content-Type: text/plain
// abcdefg
h := server.Default(server.WithStreamBody(true))
h.Post("/user", func(ctx context.Context, c *app.RequestContext) {
sr := c.RequestBodyStream()
data, _ := io.ReadAll(sr) // data == []byte("abcdefg")
})
MultipartForm
获取 multipart.Form 对象,(详情请参考 multipart#Form)
注意:此函数既可以获取普通值也可以获取文件,此处给出了获取普通值的示例代码,获取文件的示例代码可参考 MultipartForm。
函数签名:
func (ctx *RequestContext) MultipartForm() (*multipart.Form, error)
示例:
// POST http://example.com/user
// Content-Type: multipart/form-data;
// Content-Disposition: form-data; name="name"
// tom
h.POST("/user", func(ctx context.Context, c *app.RequestContext) {
form, err := c.MultipartForm()
name := form.Value["name"][0] // name == "tom"
})
PostForm
按名称检索 multipart.Form.Value,返回给定 name 的第一个值。
注意:该函数支持从 application/x-www-form-urlencoded 和 multipart/form-data 这两种类型的 content-type 中获取 value 值,且不支持获取文件值。
函数签名:
func (ctx *RequestContext) PostForm(key string) string
示例:
// POST http://example.com/user
// Content-Type: multipart/form-data;
// Content-Disposition: form-data; name="name"
// tom
h.POST("/user", func(ctx context.Context, c *app.RequestContext) {
name := c.PostForm("name") // name == "tom"
})
DefaultPostForm
按名称检索 multipart.Form.Value,返回给定 name 的第一个值,如果不存在返回 defaultValue。
注意:该函数支持从 application/x-www-form-urlencoded 和 multipart/form-data 这两种类型的 content-type 中获取 value 值,且不支持获取文件值。
函数签名:
func (ctx *RequestContext) DefaultPostForm(key, defaultValue string) string
示例:
// POST http://example.com/user
// Content-Type: multipart/form-data;
// Content-Disposition: form-data; name="name"
// tom
h.POST("/user", func(ctx context.Context, c *app.RequestContext) {
name := c.PostForm("name", "jack") // name == "tom"
age := c.PostForm("age", "10") // age == "10"
})
PostArgs
获取 application/x-www-form-urlencoded 参数对象。(详情请参考 Args 对象)
函数签名:
func (ctx *RequestContext) PostArgs() *protocol.Args
示例:
// POST http://example.com/user
// Content-Type: application/x-www-form-urlencoded
// name=tom&pet=cat&pet=dog
h.POST("/user", func(ctx context.Context, c *app.RequestContext) {
args := c.PostArgs()
name := args.Peek("name") // name == "tom"
var pets []string
args.VisitAll(func(key, value []byte) {
if string(key) == "pet" {
pets = append(pets, string(value))
}
})
// pets == []string{"cat", "dog"}
})
FormValue
按照以下顺序获取 key 的值。
- 从 QueryArgs 中获取值。
- 从 PostArgs 中获取值。
- 从 MultipartForm 中获取值。
函数签名:
func (ctx *RequestContext) FormValue(key string) []byte
示例:
// POST http://example.com/user?name=tom
// Content-Type: application/x-www-form-urlencoded
// age=10
h.POST("/user", func(ctx context.Context, c *app.RequestContext) {
name := c.FormValue("name") // name == []byte("tom"), get by QueryArgs
age := c.FormValue("age") // age == []byte("10"), get by PostArgs
})
// POST http://example.com/user
// Content-Type: multipart/form-data;
// Content-Disposition: form-data; name="name"
// tom
h.POST("/user", func(ctx context.Context, c *app.RequestContext) {
name := c.FormValue("name") // name == []byte("tom"), get by MultipartForm
})
SetFormValueFunc
若 FormValue 函数提供的默认获取 key 的值的方式不满足需求,用户可以使用该函数自定义获取 key 的值的方式。
函数签名:
func (ctx *RequestContext) SetFormValueFunc(f FormValueFunc)
示例:
// POST http://example.com/user?name=tom
// Content-Type: multipart/form-data;
// Content-Disposition: form-data; name="age"
// 10
h.POST("/user", func(ctx context.Context, c *app.RequestContext) {
// only return multipart form value
c.SetFormValueFunc(func(rc *app.RequestContext, s string) []byte {
mf, err := rc.MultipartForm()
if err == nil && mf.Value != nil {
vv := mf.Value[s]
if len(vv) > 0 {
return []byte(vv[0])
}
}
return nil
})
name := c.FormValue("name") // name == nil
age := c.FormValue("age") // age == []byte("10")
})
文件操作
func (ctx *RequestContext) MultipartForm() (*multipart.Form, error)
func (ctx *RequestContext) FormFile(name string) (*multipart.FileHeader, error)
func (ctx *RequestContext) SaveUploadedFile(file *multipart.FileHeader, dst string) error
MultipartForm
获取 multipart.Form 对象。(详情请参考 multipart#Form)
函数签名:
func (ctx *RequestContext) MultipartForm() (*multipart.Form, error)
示例:
// POST http://example.com/user
// Content-Type: multipart/form-data;
// Content-Disposition: form-data; name="avatar"; filename="abc.jpg"
h.POST("/user", func(ctx context.Context, c *app.RequestContext) {
form, err := c.MultipartForm()
avatarFile := form.File["avatar"][0] // avatarFile.Filename == "abc.jpg"
})
FormFile
按名称检索 multipart.Form.File,返回给定 name 的第一个 multipart.FileHeader。(详情请参考 multipart#FileHeader)
函数签名:
func (ctx *RequestContext) FormFile(name string) (*multipart.FileHeader, error)
示例:
// POST http://example.com/user
// Content-Type: multipart/form-data;
// Content-Disposition: form-data; name="avatar"; filename="abc.jpg"
h.Post("/user", func(ctx context.Context, c *app.RequestContext) {
avatarFile, err := c.FormFile("avatar") // avatarFile.Filename == "abc.jpg", err == nil
})
SaveUploadedFile
保存 multipart 文件到磁盘。
函数签名:
func (ctx *RequestContext) SaveUploadedFile(file *multipart.FileHeader, dst string) error
示例:
// POST http://example.com/user
// Content-Type: multipart/form-data;
// Content-Disposition: form-data; name="avatar"; filename="abc.jpg"
h.Post("/user", func(ctx context.Context, c *app.RequestContext) {
avatarFile, err := c.FormFile("avatar") // avatarFile.Filename == "abc.jpg", err == nil
// save file
c.SaveUploadedFile(avatarFile, avatarFile.Filename) // save file "abc.jpg"
})
RequestContext 元数据存储
注意:
RequestContext 在请求结束后会被回收,元数据会被置为 nil。
| 函数签名 | 说明 |
|---|---|
func (ctx *RequestContext) Set(key string, value interface{}) |
在上下文中存储键值对 |
func (ctx *RequestContext) Value(key interface{}) interface{} |
获取上下文键为 key 的值 |
func (ctx *RequestContext) Get(key string) (value interface{}, exists bool) |
获取上下文键为 key 的值以及 key 是否存在 |
func (ctx *RequestContext) MustGet(key string) interface{} |
获取上下文键为 key 的值,如果不存在会发生 panic |
func (ctx *RequestContext) GetString(key string) (s string) |
获取上下文键为 key 的值,并转换为 string 类型 |
func (ctx *RequestContext) GetBool(key string) (b bool) |
获取上下文键为 key 的值,并转换为 bool 类型 |
func (ctx *RequestContext) GetInt(key string) (i int) |
获取上下文键为 key 的值,并转换为 int 类型 |
func (ctx *RequestContext) GetInt32(key string) (i32 int32) |
获取上下文键为 key 的值,并转换为 int32 类型 |
func (ctx *RequestContext) GetInt64(key string) (i64 int64) |
获取上下文键为 key 的值,并转换为 int64 类型 |
func (ctx *RequestContext) GetUint(key string) (ui uint) |
获取上下文键为 key 的值,并转换为 uint 类型 |
func (ctx *RequestContext) GetUint32(key string) (ui32 uint32) |
获取上下文键为 key 的值,并转换为 uint32 类型 |
func (ctx *RequestContext) GetUint64(key string) (ui64 uint64) |
获取上下文键为 key 的值,并转换为 uint64 类型 |
func (ctx *RequestContext) GetFloat32(key string) (f32 float32) |
获取上下文键为 key 的值,并转换为 float32 类型 |
func (ctx *RequestContext) GetFloat64(key string) (f64 float64) |
获取上下文键为 key 的值,并转换为 float64 类型 |
func (ctx *RequestContext) GetTime(key string) (t time.Time) |
获取上下文键为 key 的值,并转换为 time.Time 类型 |
func (ctx *RequestContext) GetDuration(key string) (d time.Duration) |
获取上下文键为 key 的值,并转换为 time.Duration 类型 |
func (ctx *RequestContext) GetStringSlice(key string) (ss []string) |
获取上下文键为 key 的值,并转换为 []string 类型 |
func (ctx *RequestContext) GetStringMap(key string) (sm map[string]interface{}) |
获取上下文键为 key 的值,并转换为 map[string]interface{} 类型 |
func (ctx *RequestContext) GetStringMapString(key string) (sms map[string]string) |
获取上下文键为 key 的值,并转换为 map[string]string 类型 |
func (ctx *RequestContext) GetStringMapStringSlice(key string) (smss map[string][]string) |
获取上下文键为 key 的值,并转换为 map[string][]string 类型 |
func (ctx *RequestContext) ForEachKey(fn func(k string, v interface{})) |
为上下文中的每个键值对调用 fn |
示例:
h.GET(
"/user", func(ctx context.Context, c *app.RequestContext) {
c.Set("version1", "v1")
v := c.Value("version1") // v == interface{}(string) "v1"
c.Set("version2", "v2")
v, exists := c.Get("version2") // v == interface{}(string) "v2", exists == true
v, exists = c.Get("pet") // v == interface{} nil, exists == false
c.Set("version3", "v3")
v := c.MustGet("version3") // v == interface{}(string) "v3"
c.Set("version4", "v4")
vString := c.GetString("version4") // vString == "v4"
c.Set("isAdmin", true)
vBool := c.GetBool("isAdmin") // vBool == true
c.Set("age1", 20)
vInt := c.GetInt("age1") // vInt == 20
c.Set("age2", int32(20))
vInt32 := c.GetInt32("age2") // vInt32 == 20
c.Set("age3", int64(20))
vInt64 := c.GetInt64("age3") // vInt64 == 20
c.Set("age4", uint(20))
vUInt := c.GetUint("age4") // vUInt == 20
c.Set("age5", uint32(20))
vUInt32 := c.GetUint32("age5") // vUInt32 == 20
c.Set("age6", uint64(20))
vUInt64 := c.GetUint64("age6") // vUInt64 == 20
c.Set("age7", float32(20.1))
vFloat32 := c.GetFloat32("age7") // vFloat32 == 20.1
c.Set("age8", 20.1)
vFloat64 := c.GetFloat64("age8") // vFloat64 == 20.1
t2022, _ := time.Parse(time.RFC1123, "Wed, 21 Oct 2022 07:28:00 GMT")
c.Set("birthday", t2022)
vTime := c.GetTime("birthday") // vTime == t2022
c.Set("duration", time.Minute)
vDuration := c.GetDuration("duration") // vDuration == time.Minute
c.Set("pet", []string{"cat", "dog"})
vStringSlice := c.GetStringSlice("pet") // vStringSlice == []string{"cat", "dog"}
c.Set("info1", map[string]interface{}{"name": "tom"})
vStringMap := c.GetStringMap("info1") // vStringMap == map[string]interface{}{"name": "tom"}
c.Set("info2", map[string]string{"name": "tom"})
vStringMapString := c.GetStringMapString("info2")
// vStringMapString == map[string]string{}{"name": "tom"}
c.Set("smss", map[string][]string{"pets": {"cat", "dog"}})
vStringMapStringSlice := c.GetStringMapStringSlice("smss")
// vStringMapStringSlice == map[string][]string{"pets": {"cat", "dog"}}
c.Set("duration", time.Minute)
c.Set("version", "v1")
c.ForEachKey(
func(k string, v interface{}) {
// 1. k == "duration", v == interface{}(time.Duration) time.Minute
// 2. k == "version", v == interface{}(string) "v1"
},
)
},
)
Handler
// 继续执行下一个 Handler(通常在中间件中使用),用于将控制权交给后续的 Handler。
func (ctx *RequestContext) Next(c context.Context)
// 获取当前请求的 Handler 链(即所有待执行的 Handler 列表)。
func (ctx *RequestContext) Handlers() HandlersChain
// 获取当前正在执行的 Handler 函数。
func (ctx *RequestContext) Handler() HandlerFunc
// 设置当前请求的 Handler 链(覆盖原有的 Handler 链)。
func (ctx *RequestContext) SetHandlers(hc HandlersChain)
// 获取当前正在执行的 Handler 的名称(通常是函数名),用于调试或日志记录。
func (ctx *RequestContext) HandlerName() string
// 获取当前执行的 Handler 在 Handler 链中的索引位置(从 0 开始)。
func (ctx *RequestContext) GetIndex() int8
// 终止当前请求的处理流程,后续的 Handler 将不会被执行(通常在中间件中使用)。
func (ctx *RequestContext) Abort()
// 检查当前请求是否已被终止(即是否调用了 Abort())。
func (ctx *RequestContext) IsAborted() bool
-
中间件中使用
Next和Abort:func AuthMiddleware() app.HandlerFunc { return func(c context.Context, ctx *app.RequestContext) { if !checkAuth(ctx) { ctx.Abort() // 验证失败,终止请求 ctx.String(401, "Unauthorized") return } ctx.Next(c) // 验证通过,继续执行下一个 Handler } } -
动态修改 Handler 链:
func DynamicHandler() app.HandlerFunc { return func(c context.Context, ctx *app.RequestContext) { if someCondition { newHandlers := someNewHandlersChain ctx.SetHandlers(newHandlers) // 动态替换 Handler 链 } ctx.Next(c) } } -
调试 Handler 执行流程:
func DebugMiddleware() app.HandlerFunc { return func(c context.Context, ctx *app.RequestContext) { fmt.Printf("Current Handler: %s, Index: %d\n", ctx.HandlerName(), ctx.GetIndex()) ctx.Next(c) } }
参数绑定与校验
https://www.cloudwego.io/zh/docs/hertz/tutorials/basic-feature/binding-and-validate/
Hertz 支持的参数绑定与校验相关功能及用法。
func (ctx *RequestContext) Bind(obj interface{}) error
func (ctx *RequestContext) Validate(obj interface{}) error
func (ctx *RequestContext) BindAndValidate(obj interface{}) error
使用方法
func main() {
r := server.New()
r.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
// 参数绑定需要配合特定的 go tag 使用
type Test struct {
A string `query:"a" vd:"$!='Hertz'"`
}
// BindAndValidate
var req Test
err := c.BindAndValidate(&req)
...
// Bind 只做参数绑定
req = Test{}
err = c.Bind(&req)
...
// Validate,需要使用 "vd" tag
err = c.Validate(&req)
...
})
...
}
全部 API
hertz version >= v0.7.0
| API | 说明 |
|---|---|
| ctx.BindAndValidate | 利用下述的 go-tag 进行参数绑定,并在绑定成功后做一次参数校验 (如果有校验 tag 的话) |
| ctx.Bind | 同 BindAndValidate 但是不做参数校验 |
| ctx.BindQuery | 绑定所有 Query 参数,相当于给每一个 field 声明一个 query tag,适用于没写 tag 的场景 |
| ctx.BindHeader | 绑定所有 Header 参数,相当于给每一个 field 声明一个 header tag,适用于没写 tag 的场景 |
| ctx.BindPath | 绑定所有 Path 参数,相当于给每一个 field 声明一个 path tag,适用于没写 tag 的场景 |
| ctx.BindForm | 绑定所有 Form 参数,相当于给每一个 field 声明一个 form tag,需要 Content-Type 为:application/x-www-form-urlencoded/multipart/form-data, 适用于没写 tag 的场景 |
| ctx.BindJSON | 绑定 JSON Body,调用 json.Unmarshal() 进行反序列化,需要 Body 为 application/json 格式 |
| ctx.BindProtobuf | 绑定 Protobuf Body,调用 proto.Unmarshal() 进行反序列化,需要 Body 为 application/x-protobuf 格式 |
| ctx.BindByContentType | 根据 Content-Type 来自动选择绑定的方法,其中 GET 请求会调用 BindQuery, 带有 Body 的请求会根据 Content-Type 自动选择 |
| ctx.Validate | 进行参数校验,需要校验 tag 配合使用 (默认使用 vd tag 校验) |
支持的 tag 及参数绑定优先级
支持的 tag
不通过 IDL 生成代码时若字段不添加任何 tag 则会遍历所有 tag 并按照优先级绑定参数,添加 tag 则会根据对应的 tag 按照优先级去绑定参数。
通过 IDL 生成代码时若不添加 api 注解 则字段默认添加 form、json、query tag,添加 api 注解 会为字段添加相应需求的 tag。
| go tag | 说明 |
|---|---|
| path | 绑定 url 上的路径参数,相当于 hertz 路由 :param 或 *param 中拿到的参数。例如:如果定义的路由为:/v:version/example,可以把 path 的参数指定为路由参数:path:"version",此时,url: http://127.0.0.1:8888/v1/example,可以绑定path参数"1" |
| form | 绑定请求的 body 内容。content-type -> multipart/form-data 或 application/x-www-form-urlencoded,绑定 form 的 key-value |
| query | 绑定请求的 query 参数 |
| cookie | 绑定请求的 cookie 参数 |
| header | 绑定请求的 header 参数 |
| json | 绑定请求的 body 内容 content-type -> application/json,绑定 json 参数 |
| raw_body | 绑定请求的原始 body(bytes),绑定的字段名不指定,也能绑定参数。(注:raw_body 绑定优先级最低,当指定多个 tag 时,一旦其他 tag 成功绑定参数,则不会绑定 body 内容。) |
| vd | 参数校验,校验语法 |
| default | 设置默认值 |
参数校验
具体校验语法可参考 校验语法。
不通过 IDL 生成代码时直接在对应结构体字段打 tag,示例:
type InfoRequest struct {
Name string `vd:"$!='your string'"`
}
通过 IDL 生成代码时需添加相应的注解,可参考 Field 注解。
下面给出常见用法:
- string 和 list 的长度验证
len($)>0 - 字符串正则匹配
regexp('^\\w*$')" - 验证数字字段的的值
$>0 - 验证指针字段
num==nil || num>0 - 验证枚举类型
type=="hello" || type == "world" - 自定义错误信息
msg:'C must be false when S.A>0'"
参数绑定优先级
path > form > query > cookie > header > json > raw_body
注:如果请求的 content-type 为
application/json,使用BindAndValidate,Bind方法会在参数绑定前做一次 json unmarshal 处理。
必传参数
通过在 tag 中添加 required,可以将参数标记为必传。当绑定失败时 Bind 和 BindAndValidate 将会返回错误。当多个 tag 包含 required 时,将会按照优先级绑定。如果所有 tag 都没有绑定上,则会返回错误。
type TagRequiredReq struct {
// 当 JSON 中没有 hertz 字段时,会返回 required 错误
Hertz string `json:"hertz,required"`
// 当 query 和 JSON 中同时没有 kitex 字段时,会返回 required 错误
Kitex string `query:"kitex,required" json:"kitex,required" `
}
获取 ClientIP
func (ctx *RequestContext) ClientIP() string
func (ctx *RequestContext) SetClientIPFunc(f ClientIP)
ClientIP
获取客户端 IP 的地址。
该函数的默认行为:若 X-Forwarded-For 或 X-Real-IP Header 中存在 ip,则从这两个 Header 中读 ip 并返回(优先级 X-Forwarded-For 大于 X-Real-IP),否则返回 remote address。
函数签名:
func (ctx *RequestContext) ClientIP() string
示例:
// X-Forwarded-For: 20.20.20.20, 30.30.30.30
// X-Real-IP: 10.10.10.10
h.Use(func(ctx context.Context, c *app.RequestContext) {
ip := c.ClientIP() // 20.20.20.20
})
SetClientIPFunc
若 ClientIP 函数提供的默认方式不满足需求,用户可以使用该函数自定义获取客户端 ip 的方式。
用户可以自己实现自定义函数,也可以通过设置 app.ClientIPOptions 实现。
注意:
在设置
app.ClientIPOptions时,TrustedCIDRs需用户自定义(若不设置则固定返回 remote address),代表可信任的路由。若 remote address 位于可信任的路由范围内,则会选择从
RemoteIPHeaders中获取 ip,否则返回 remote address。
函数签名:
func (ctx *RequestContext) SetClientIPFunc(f ClientIP)
示例:
// POST http://example.com/user
// X-Forwarded-For: 30.30.30.30
h.POST("/user", func(ctx context.Context, c *app.RequestContext) {
// method 1
customClientIPFunc := func(c *app.RequestContext) string {
return "127.0.0.1"
}
c.SetClientIPFunc(customClientIPFunc)
ip := c.ClientIP() // ip == "127.0.0.1"
// method 2
_, cidr, _ := net.ParseCIDR("127.0.0.1/32")
opts := app.ClientIPOptions{
RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"},
TrustedCIDRs: []*net.IPNet{cidr},
}
c.SetClientIPFunc(app.ClientIPWithOption(opts))
ip = c.ClientIP() // ip == "30.30.30.30"
})
并发安全
拷贝 RequestContext 副本,提供协程安全的访问方式。
func (ctx *RequestContext) Copy() *RequestContext
示例:
h.POST("/user", func(ctx context.Context, c *app.RequestContext) {
ctx1 := c.Copy()
go func(context *app.RequestContext) {
// safely
}(ctx1)
})
本文来自博客园,作者:厚礼蝎,转载请注明原文链接:https://www.cnblogs.com/guangdelw/p/18737964

浙公网安备 33010602011771号