1
0
mirror of https://github.com/gogf/gf.git synced 2025-04-04 10:32:46 +08:00

feat(net/ghttp): add Request.GetMetaTag to retrieve specific meta tag value (#4185)

This commit is contained in:
PandaPy 2025-03-09 11:17:41 +08:00 committed by GitHub
parent bcda48bf82
commit f8331bad6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 96 additions and 15 deletions

View File

@ -3,7 +3,7 @@
coverage=$1
# update code of submodules
make subup
git clone https://github.com/gogf/examples
# find all path that contains go.mod.
for file in `find . -name go.mod`; do
@ -22,14 +22,14 @@ for file in `find . -name go.mod`; do
fi
# Check if it's a contrib directory or examples directory
if [[ $dirpath =~ "/contrib/" ]] || [ "examples" = $(basename $dirpath) ]; then
if [[ $dirpath =~ "/contrib/" ]] || [[ $dirpath =~ "/examples/" ]]; then
# Check if go version meets the requirement
if ! go version | grep -qE "go${LATEST_GO_VERSION}"; then
echo "ignore path $dirpath as go version is not ${LATEST_GO_VERSION}: $(go version)"
continue 1
fi
# If it's examples directory, only build without tests
if [ "examples" = $(basename $dirpath) ]; then
if [[ $dirpath =~ "/examples/" ]]; then
echo "the examples directory only needs to be built, not unit tests and coverage tests."
cd $dirpath
go mod tidy

@ -1 +1 @@
Subproject commit c454db2549ac7ecce844b687c1e610461818830d
Subproject commit bf0ab5ac16fbf654fc424baf45e96e541a037cb3

View File

@ -19,6 +19,7 @@ import (
"github.com/gogf/gf/v2/os/gview"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gmeta"
"github.com/gogf/gf/v2/util/guid"
)
@ -40,7 +41,7 @@ type Request struct {
// =================================================================================================================
handlers []*HandlerItemParsed // All matched handlers containing handler, hook and middleware for this request.
serveHandler *HandlerItemParsed // Real handler serving for this request, not hook or middleware.
serveHandler *HandlerItemParsed // Real business handler serving for this request, not hook or middleware handler.
handlerResponse interface{} // Handler response object for Request/Response handler.
hasHookHandler bool // A bool marking whether there's hook handler in the handlers for performance purpose.
hasServeHandler bool // A bool marking whether there's serving handler in the handlers for performance purpose.
@ -288,3 +289,25 @@ func (r *Request) GetHandlerResponse() interface{} {
func (r *Request) GetServeHandler() *HandlerItemParsed {
return r.serveHandler
}
// GetMetaTag retrieves and returns the metadata value associated with the given key from the request struct.
// The meta value is from struct tags from g.Meta/gmeta.Meta type.
// For example:
//
// type GetMetaTagReq struct {
// g.Meta `path:"/test" method:"post" summary:"meta_tag" tags:"meta"`
// // ...
// }
//
// r.GetMetaTag("summary") // returns "meta_tag"
// r.GetMetaTag("method") // returns "post"
func (r *Request) GetMetaTag(key string) string {
if r.serveHandler == nil || r.serveHandler.Handler == nil {
return ""
}
metaValue := gmeta.Get(r.serveHandler.Handler.Info.Type.In(1), key)
if metaValue != nil {
return metaValue.String()
}
return ""
}

View File

@ -8,6 +8,7 @@ package ghttp_test
import (
"bytes"
"context"
"fmt"
"io"
"testing"
@ -861,3 +862,43 @@ func Test_Params_GetRequestMapStrVar(t *testing.T) {
t.Assert(client.GetContent(ctx, "/GetRequestMapStrVar", "id=1"), 1)
})
}
type GetMetaTagReq struct {
g.Meta `path:"/test" method:"post" summary:"meta_tag" tags:"meta"`
Name string
}
type GetMetaTagRes struct{}
type GetMetaTagSt struct{}
func (r GetMetaTagSt) PostTest(ctx context.Context, req *GetMetaTagReq) (*GetMetaTagRes, error) {
return &GetMetaTagRes{}, nil
}
func TestRequest_GetMetaTag(t *testing.T) {
s := g.Server(guid.S())
s.Use(func(r *ghttp.Request) {
r.Response.Writef(
"summary:%s,method:%s",
r.GetMetaTag("summary"), r.GetMetaTag("method"),
)
})
s.Group("/", func(grp *ghttp.RouterGroup) {
grp.Bind(GetMetaTagSt{})
})
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(1000 * time.Millisecond)
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(100 * time.Millisecond)
gtest.C(t, func(t *gtest.T) {
client := g.Client()
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
t.Assert(client.PostContent(ctx, "/test", "name=john"), "summary:meta_tag,method:post")
})
}

View File

@ -36,7 +36,7 @@ type Field struct {
type FieldsInput struct {
// Pointer should be type of struct/*struct.
// TODO this attribute name is not suitable, which would make confuse.
Pointer interface{}
Pointer any
// RecursiveOption specifies the way retrieving the fields recursively if the attribute
// is an embedded struct. It is RecursiveOptionNone in default.
@ -47,7 +47,7 @@ type FieldsInput struct {
type FieldMapInput struct {
// Pointer should be type of struct/*struct.
// TODO this attribute name is not suitable, which would make confuse.
Pointer interface{}
Pointer any
// PriorityTagArray specifies the priority tag array for retrieving from high to low.
// If it's given `nil`, it returns map[name]Field, of which the `name` is attribute name.
@ -123,6 +123,7 @@ func Fields(in FieldsInput) ([]Field, error) {
}
}
continue
default:
}
}
continue
@ -194,6 +195,7 @@ func FieldMap(in FieldMapInput) (map[string]Field, error) {
mapField[k] = tempV
}
}
default:
}
} else {
mapField[field.Name()] = tempField
@ -205,7 +207,19 @@ func FieldMap(in FieldMapInput) (map[string]Field, error) {
// StructType retrieves and returns the struct Type of specified struct/*struct.
// The parameter `object` should be either type of struct/*struct/[]struct/[]*struct.
func StructType(object interface{}) (*Type, error) {
func StructType(object any) (*Type, error) {
// if already reflect.Type
if reflectType, ok := object.(reflect.Type); ok {
for reflectType.Kind() == reflect.Ptr {
reflectType = reflectType.Elem()
}
if reflectType.Kind() == reflect.Struct {
return &Type{
Type: reflectType,
}, nil
}
}
var (
reflectValue reflect.Value
reflectKind reflect.Kind

View File

@ -8,6 +8,8 @@
package gmeta
import (
"reflect"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/os/gstructs"
)
@ -15,19 +17,20 @@ import (
// Meta is used as an embedded attribute for struct to enabled metadata feature.
type Meta struct{}
const (
metaAttributeName = "Meta" // metaAttributeName is the attribute name of metadata in struct.
metaTypeName = "gmeta.Meta" // metaTypeName is for type string comparison.
)
// metaAttributeName is the attribute name of metadata in struct.
const metaAttributeName = "Meta"
// metaType holds the reflection. Type of Meta, used for efficient type comparison.
var metaType = reflect.TypeOf(Meta{})
// Data retrieves and returns all metadata from `object`.
func Data(object interface{}) map[string]string {
func Data(object any) map[string]string {
reflectType, err := gstructs.StructType(object)
if err != nil {
return nil
}
if field, ok := reflectType.FieldByName(metaAttributeName); ok {
if field.Type.String() == metaTypeName {
if field.Type == metaType {
return gstructs.ParseTag(string(field.Tag))
}
}
@ -35,7 +38,7 @@ func Data(object interface{}) map[string]string {
}
// Get retrieves and returns specified metadata by `key` from `object`.
func Get(object interface{}, key string) *gvar.Var {
func Get(object any, key string) *gvar.Var {
v, ok := Data(object)[key]
if !ok {
return nil