深入理解 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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2023-02-26 go结构体打印格式化成json
2023-02-26 openEuler22.09初始化脚本