mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
improve package ghttp and internal/structs
This commit is contained in:
@ -22,6 +22,21 @@ import (
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func MapField(pointer interface{}, priority []string, recursive bool) map[string]*Field {
|
||||
// If <pointer> points to an invalid address, for example a nil variable,
|
||||
// it here creates an empty struct using reflect feature.
|
||||
var (
|
||||
tempValue reflect.Value
|
||||
pointerValue = reflect.ValueOf(pointer)
|
||||
)
|
||||
for pointerValue.Kind() == reflect.Ptr {
|
||||
tempValue = pointerValue.Elem()
|
||||
if !tempValue.IsValid() {
|
||||
pointer = reflect.New(pointerValue.Type().Elem()).Elem()
|
||||
break
|
||||
} else {
|
||||
pointerValue = tempValue
|
||||
}
|
||||
}
|
||||
var (
|
||||
fields []*structs.Field
|
||||
fieldMap = make(map[string]*Field)
|
||||
@ -59,8 +74,10 @@ func MapField(pointer interface{}, priority []string, recursive bool) map[string
|
||||
}
|
||||
}
|
||||
if recursive {
|
||||
rv := reflect.ValueOf(field.Value())
|
||||
kind := rv.Kind()
|
||||
var (
|
||||
rv = reflect.ValueOf(field.Value())
|
||||
kind = rv.Kind()
|
||||
)
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
|
||||
@ -27,6 +27,21 @@ func TagFields(pointer interface{}, priority []string, recursive bool) []*Field
|
||||
// tag internally.
|
||||
// The parameter <pointer> should be type of struct/*struct.
|
||||
func doTagFields(pointer interface{}, priority []string, recursive bool, tagMap map[string]struct{}) []*Field {
|
||||
// If <pointer> points to an invalid address, for example a nil variable,
|
||||
// it here creates an empty struct using reflect feature.
|
||||
var (
|
||||
tempValue reflect.Value
|
||||
pointerValue = reflect.ValueOf(pointer)
|
||||
)
|
||||
for pointerValue.Kind() == reflect.Ptr {
|
||||
tempValue = pointerValue.Elem()
|
||||
if !tempValue.IsValid() {
|
||||
pointer = reflect.New(pointerValue.Type().Elem()).Elem()
|
||||
break
|
||||
} else {
|
||||
pointerValue = tempValue
|
||||
}
|
||||
}
|
||||
var fields []*structs.Field
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
fields = structs.Fields(v.Interface())
|
||||
|
||||
@ -71,3 +71,20 @@ func Test_Basic(t *testing.T) {
|
||||
t.Assert(structs.TagMapName(user2, []string{"params"}, true), g.Map{"password1": "Pass1", "password2": "Pass2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StructOfNilPointer(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Name string `params:"name"`
|
||||
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
|
||||
}
|
||||
var user *User
|
||||
t.Assert(structs.TagMapName(user, []string{"params"}, true), g.Map{"name": "Name", "pass": "Pass"})
|
||||
t.Assert(structs.TagMapName(&user, []string{"params"}, true), g.Map{"name": "Name", "pass": "Pass"})
|
||||
|
||||
t.Assert(structs.TagMapName(&user, []string{"params", "my-tag1"}, true), g.Map{"name": "Name", "pass": "Pass"})
|
||||
t.Assert(structs.TagMapName(&user, []string{"my-tag1", "params"}, true), g.Map{"name": "Name", "pass1": "Pass"})
|
||||
t.Assert(structs.TagMapName(&user, []string{"my-tag2", "params"}, true), g.Map{"name": "Name", "pass2": "Pass"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -25,6 +25,12 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
parseTypeRequest = 0
|
||||
parseTypeQuery = 1
|
||||
parseTypeForm = 2
|
||||
)
|
||||
|
||||
var (
|
||||
// xmlHeaderBytes is the most common XML format header.
|
||||
xmlHeaderBytes = []byte("<?xml")
|
||||
@ -37,11 +43,26 @@ var (
|
||||
// The parameter <pointer> can be type of: *struct/**struct/*[]struct/*[]*struct.
|
||||
//
|
||||
// It supports single and multiple struct convertion:
|
||||
// 1. Single struct, post content like: {"id":1, "name":"john"}
|
||||
// 1. Single struct, post content like: {"id":1, "name":"john"} or ?id=1&name=john
|
||||
// 2. Multiple struct, post content like: [{"id":1, "name":"john"}, {"id":, "name":"smith"}]
|
||||
//
|
||||
// TODO: Improve the performance by reducing duplicated reflect usage on the same variable across packages.
|
||||
func (r *Request) Parse(pointer interface{}) error {
|
||||
return r.doParse(pointer, parseTypeRequest)
|
||||
}
|
||||
|
||||
// ParseQuery performs like function Parse, but only parses the query parameters.
|
||||
func (r *Request) ParseQuery(pointer interface{}) error {
|
||||
return r.doParse(pointer, parseTypeQuery)
|
||||
}
|
||||
|
||||
// ParseForm performs like function Parse, but only parses the form parameters or the body content.
|
||||
func (r *Request) ParseForm(pointer interface{}) error {
|
||||
return r.doParse(pointer, parseTypeForm)
|
||||
}
|
||||
|
||||
// doParse parses the request data to struct/structs according to request type.
|
||||
func (r *Request) doParse(pointer interface{}, requestType int) error {
|
||||
var (
|
||||
reflectVal1 = reflect.ValueOf(pointer)
|
||||
reflectKind1 = reflectVal1.Kind()
|
||||
@ -58,18 +79,31 @@ func (r *Request) Parse(pointer interface{}) error {
|
||||
)
|
||||
switch reflectKind2 {
|
||||
// Single struct, post content like:
|
||||
// {"id":1, "name":"john"}
|
||||
// 1. {"id":1, "name":"john"}
|
||||
// 2. ?id=1&name=john
|
||||
case reflect.Ptr, reflect.Struct:
|
||||
// Conversion.
|
||||
if err := r.GetStruct(pointer); err != nil {
|
||||
return err
|
||||
// Converting.
|
||||
switch requestType {
|
||||
case parseTypeQuery:
|
||||
if err := r.GetQueryStruct(pointer); err != nil {
|
||||
return err
|
||||
}
|
||||
case parseTypeForm:
|
||||
if err := r.GetFormStruct(pointer); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if err := r.GetStruct(pointer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Validation.
|
||||
if err := gvalid.CheckStruct(pointer, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Multiple struct, post content like:
|
||||
// Multiple struct, it only supports JSON type post content like:
|
||||
// [{"id":1, "name":"john"}, {"id":, "name":"smith"}]
|
||||
case reflect.Array, reflect.Slice:
|
||||
// If struct slice conversion, it might post JSON/XML content,
|
||||
@ -371,11 +405,14 @@ func (r *Request) parseForm() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.formMap == nil {
|
||||
}
|
||||
// It parses the request body without checking the Content-Type.
|
||||
if r.formMap == nil {
|
||||
if r.Method != "GET" {
|
||||
r.parseBody()
|
||||
if len(r.bodyMap) > 0 {
|
||||
r.formMap = r.bodyMap
|
||||
}
|
||||
}
|
||||
if len(r.bodyMap) > 0 {
|
||||
r.formMap = r.bodyMap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +34,9 @@ func (r *Request) GetQuery(key string, def ...interface{}) interface{} {
|
||||
return v
|
||||
}
|
||||
}
|
||||
r.parseBody()
|
||||
if r.Method == "GET" {
|
||||
r.parseBody()
|
||||
}
|
||||
if len(r.bodyMap) > 0 {
|
||||
if v, ok := r.bodyMap[key]; ok {
|
||||
return v
|
||||
@ -118,7 +120,9 @@ func (r *Request) GetQueryInterfaces(key string, def ...interface{}) []interface
|
||||
// in order of priority: query > body.
|
||||
func (r *Request) GetQueryMap(kvMap ...map[string]interface{}) map[string]interface{} {
|
||||
r.parseQuery()
|
||||
r.parseBody()
|
||||
if r.Method == "GET" {
|
||||
r.parseBody()
|
||||
}
|
||||
var m map[string]interface{}
|
||||
if len(kvMap) > 0 && kvMap[0] != nil {
|
||||
if len(r.queryMap) == 0 && len(r.bodyMap) == 0 {
|
||||
|
||||
@ -27,13 +27,11 @@ func Test_Params_Parse(t *testing.T) {
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/parse", func(r *ghttp.Request) {
|
||||
if m := r.GetMap(); len(m) > 0 {
|
||||
var user *User
|
||||
if err := r.Parse(&user); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit(user.Map["id"], user.Map["score"])
|
||||
var user *User
|
||||
if err := r.Parse(&user); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit(user.Map["id"], user.Map["score"])
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouterMap(false)
|
||||
@ -48,7 +46,76 @@ func Test_Params_Parse(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_Parse2(t *testing.T) {
|
||||
func Test_Params_ParseQuery(t *testing.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/parse-query", func(r *ghttp.Request) {
|
||||
var user *User
|
||||
if err := r.ParseQuery(&user); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit(user.Id, user.Name)
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
c := g.Client()
|
||||
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
t.Assert(c.GetContent("/parse-query"), `0`)
|
||||
t.Assert(c.GetContent("/parse-query?id=1&name=john"), `1john`)
|
||||
t.Assert(c.PostContent("/parse-query"), `0`)
|
||||
t.Assert(c.PostContent("/parse-query", g.Map{
|
||||
"id": 1,
|
||||
"name": "john",
|
||||
}), `0`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_ParseForm(t *testing.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/parse-form", func(r *ghttp.Request) {
|
||||
var user *User
|
||||
if err := r.ParseForm(&user); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit(user.Id, user.Name)
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
c := g.Client()
|
||||
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
t.Assert(c.GetContent("/parse-form"), `0`)
|
||||
t.Assert(c.GetContent("/parse-form", g.Map{
|
||||
"id": 1,
|
||||
"name": "john",
|
||||
}), 0)
|
||||
t.Assert(c.PostContent("/parse-form"), `0`)
|
||||
t.Assert(c.PostContent("/parse-form", g.Map{
|
||||
"id": 1,
|
||||
"name": "john",
|
||||
}), `1john`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_ComplexJsonStruct(t *testing.T) {
|
||||
type ItemEnv struct {
|
||||
Type string
|
||||
Key string
|
||||
|
||||
@ -433,10 +433,10 @@ func Test_Params_SupportChars(t *testing.T) {
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/form-value", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetQuery("test-value"))
|
||||
r.Response.Write(r.GetForm("test-value"))
|
||||
})
|
||||
s.BindHandler("/form-array", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetQuery("test-array"))
|
||||
r.Response.Write(r.GetForm("test-array"))
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouterMap(false)
|
||||
@ -445,12 +445,10 @@ func Test_Params_SupportChars(t *testing.T) {
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", p)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.PostContent("/form-value", "test-value=100"), "100")
|
||||
t.Assert(client.PostContent("/form-array", "test-array[]=1&test-array[]=2"), `["1","2"]`)
|
||||
c := g.Client()
|
||||
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
t.Assert(c.PostContent("/form-value", "test-value=100"), "100")
|
||||
t.Assert(c.PostContent("/form-array", "test-array[]=1&test-array[]=2"), `["1","2"]`)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user