// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. package ghttp_test import ( "fmt" "testing" "time" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/test/gtest" "github.com/gogf/gf/v2/util/guid" "github.com/gogf/gf/v2/util/gvalid" ) func Test_Params_Parse(t *testing.T) { type User struct { Id int Name string Map map[string]interface{} } s := g.Server(guid.S()) s.BindHandler("/parse", func(r *ghttp.Request) { var user *User if err := r.Parse(&user); err != nil { r.Response.WriteExit(err) } r.Response.WriteExit(user.Map["id"], user.Map["score"]) }) 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, "/parse", `{"id":1,"name":"john","map":{"id":1,"score":100}}`), `1100`) }) } func Test_Params_ParseQuery(t *testing.T) { type User struct { Id int Name string } s := g.Server(guid.S()) 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.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", s.GetListenedPort())) t.Assert(c.GetContent(ctx, "/parse-query"), `0`) t.Assert(c.GetContent(ctx, "/parse-query?id=1&name=john"), `1john`) t.Assert(c.PostContent(ctx, "/parse-query"), `0`) t.Assert(c.PostContent(ctx, "/parse-query", g.Map{ "id": 1, "name": "john", }), `0`) }) } func Test_Params_ParseForm(t *testing.T) { type User struct { Id int Name string } s := g.Server(guid.S()) 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.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", s.GetListenedPort())) t.Assert(c.GetContent(ctx, "/parse-form"), `0`) t.Assert(c.GetContent(ctx, "/parse-form", g.Map{ "id": 1, "name": "john", }), 0) t.Assert(c.PostContent(ctx, "/parse-form"), `0`) t.Assert(c.PostContent(ctx, "/parse-form", g.Map{ "id": 1, "name": "john", }), `1john`) }) } func Test_Params_ComplexJsonStruct(t *testing.T) { type ItemEnv struct { Type string Key string Value string Brief string } type ItemProbe struct { Type string Port int Path string Brief string Period int InitialDelay int TimeoutSeconds int } type ItemKV struct { Key string Value string } type ItemPort struct { Port int Type string Alias string Brief string } type ItemMount struct { Type string DstPath string Src string SrcPath string Brief string } type SaveRequest struct { AppId uint Name string Type string Cluster string Replicas uint ContainerName string ContainerImage string VersionTag string Namespace string Id uint Status uint Metrics string InitImage string CpuRequest uint CpuLimit uint MemRequest uint MemLimit uint MeshEnabled uint ContainerPorts []ItemPort Labels []ItemKV NodeSelector []ItemKV EnvReserve []ItemKV EnvGlobal []ItemEnv EnvContainer []ItemEnv Mounts []ItemMount LivenessProbe ItemProbe ReadinessProbe ItemProbe } s := g.Server(guid.S()) s.BindHandler("/parse", func(r *ghttp.Request) { if m := r.GetMap(); len(m) > 0 { var data *SaveRequest if err := r.Parse(&data); err != nil { r.Response.WriteExit(err) } r.Response.WriteExit(data) } }) 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())) content := ` { "app_id": 5, "cluster": "test", "container_image": "nginx", "container_name": "test", "container_ports": [ { "alias": "别名", "brief": "描述", "port": 80, "type": "tcp" } ], "cpu_limit": 100, "cpu_request": 10, "create_at": "2020-10-10 12:00:00", "creator": 1, "env_container": [ { "brief": "用户环境变量", "key": "NAME", "type": "string", "value": "john" } ], "env_global": [ { "brief": "数据数量", "key": "NUMBER", "type": "string", "value": "1" } ], "env_reserve": [ { "key": "NODE_IP", "value": "status.hostIP" } ], "liveness_probe": { "brief": "存活探针", "initial_delay": 10, "path": "", "period": 5, "port": 80, "type": "tcpSocket" }, "readiness_probe": { "brief": "就绪探针", "initial_delay": 10, "path": "", "period": 5, "port": 80, "type": "tcpSocket" }, "id": 0, "init_image": "", "labels": [ { "key": "app", "value": "test" } ], "mem_limit": 1000, "mem_request": 100, "mesh_enabled": 0, "metrics": "", "mounts": [], "name": "test", "namespace": "test", "node_selector": [ { "key": "group", "value": "app" } ], "replicas": 1, "type": "test", "update_at": "2020-10-10 12:00:00", "version_tag": "test" } ` t.Assert(client.PostContent(ctx, "/parse", content), `{"AppId":5,"Name":"test","Type":"test","Cluster":"test","Replicas":1,"ContainerName":"test","ContainerImage":"nginx","VersionTag":"test","Namespace":"test","Id":0,"Status":0,"Metrics":"","InitImage":"","CpuRequest":10,"CpuLimit":100,"MemRequest":100,"MemLimit":1000,"MeshEnabled":0,"ContainerPorts":[{"Port":80,"Type":"tcp","Alias":"别名","Brief":"描述"}],"Labels":[{"Key":"app","Value":"test"}],"NodeSelector":[{"Key":"group","Value":"app"}],"EnvReserve":[{"Key":"NODE_IP","Value":"status.hostIP"}],"EnvGlobal":[{"Type":"string","Key":"NUMBER","Value":"1","Brief":"数据数量"}],"EnvContainer":[{"Type":"string","Key":"NAME","Value":"john","Brief":"用户环境变量"}],"Mounts":[],"LivenessProbe":{"Type":"tcpSocket","Port":80,"Path":"","Brief":"存活探针","Period":5,"InitialDelay":10,"TimeoutSeconds":0},"ReadinessProbe":{"Type":"tcpSocket","Port":80,"Path":"","Brief":"就绪探针","Period":5,"InitialDelay":10,"TimeoutSeconds":0}}`) }) } func Test_Params_Parse_Attr_Pointer1(t *testing.T) { type User struct { Id *int Name *string } s := g.Server(guid.S()) s.BindHandler("/parse1", 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.Id, user.Name) } }) s.BindHandler("/parse2", func(r *ghttp.Request) { if m := r.GetMap(); len(m) > 0 { var user = new(User) if err := r.Parse(user); err != nil { r.Response.WriteExit(err) } r.Response.WriteExit(user.Id, user.Name) } }) 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, "/parse1", `{"id":1,"name":"john"}`), `1john`) t.Assert(client.PostContent(ctx, "/parse2", `{"id":1,"name":"john"}`), `1john`) t.Assert(client.PostContent(ctx, "/parse2?id=1&name=john"), `1john`) t.Assert(client.PostContent(ctx, "/parse2", `id=1&name=john`), `1john`) }) } func Test_Params_Parse_Attr_Pointer2(t *testing.T) { type User struct { Id *int `v:"required"` } s := g.Server(guid.S()) s.BindHandler("/parse", func(r *ghttp.Request) { var user *User if err := r.Parse(&user); err != nil { r.Response.WriteExit(err.Error()) } r.Response.WriteExit(user.Id) }) 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, "/parse"), `The Id field is required`) t.Assert(client.PostContent(ctx, "/parse?id=1"), `1`) }) } // It does not support this kind of converting yet. // func Test_Params_Parse_Attr_SliceSlice(t *testing.T) { // type User struct { // Id int // Name string // Scores [][]int // } // // s := g.Server(guid.S()) // 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.Scores) // } // }) // // 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, "/parse", `{"id":1,"name":"john","scores":[[1,2,3]]}`), `1100`) // }) // } func Test_Params_Struct(t *testing.T) { type User struct { Id int Name string Time *time.Time Pass1 string `p:"password1"` Pass2 string `p:"password2" v:"password2 @required|length:2,20|password3#||密码强度不足"` } s := g.Server(guid.S()) s.BindHandler("/struct1", func(r *ghttp.Request) { if m := r.GetMap(); len(m) > 0 { user := new(User) if err := r.GetStruct(user); err != nil { r.Response.WriteExit(err) } r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2) } }) s.BindHandler("/struct2", func(r *ghttp.Request) { if m := r.GetMap(); len(m) > 0 { user := (*User)(nil) if err := r.GetStruct(&user); err != nil { r.Response.WriteExit(err) } if user != nil { r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2) } } }) s.BindHandler("/struct-valid", func(r *ghttp.Request) { if m := r.GetMap(); len(m) > 0 { user := new(User) if err := r.GetStruct(user); err != nil { r.Response.WriteExit(err) } if err := gvalid.New().Data(user).Run(r.Context()); err != nil { r.Response.WriteExit(err) } } }) 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.Id, user.Name, user.Pass1, user.Pass2) } }) 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.GetContent(ctx, "/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`) t.Assert(client.PostContent(ctx, "/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`) t.Assert(client.PostContent(ctx, "/struct2", `id=1&name=john&password1=123&password2=456`), `1john123456`) t.Assert(client.PostContent(ctx, "/struct2", ``), ``) t.Assert(client.PostContent(ctx, "/struct-valid", `id=1&name=john&password1=123&password2=0`), "The password2 value `0` length must be between 2 and 20; 密码强度不足") t.Assert(client.PostContent(ctx, "/parse", `id=1&name=john&password1=123&password2=0`), "The password2 value `0` length must be between 2 and 20") t.Assert(client.PostContent(ctx, "/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john123Abc!@#123Abc!@#`) }) } func Test_Params_Structs(t *testing.T) { type User struct { Id int Name string Time *time.Time Pass1 string `p:"password1"` Pass2 string `p:"password2" v:"password2 @required|length:2,20|password3#||密码强度不足"` } s := g.Server(guid.S()) s.BindHandler("/parse1", func(r *ghttp.Request) { var users []*User if err := r.Parse(&users); err != nil { r.Response.WriteExit(err) } r.Response.WriteExit(users[0].Id, users[1].Id) }) 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, "/parse1", `[{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}, {"id":2,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}]`), `12`, ) }) } func Test_Params_Struct_Validation(t *testing.T) { type User struct { Id int `v:"required"` Name string `v:"name@required-with:id"` } s := g.Server(guid.S()) s.Group("/", func(group *ghttp.RouterGroup) { group.ALL("/", func(r *ghttp.Request) { var ( err error user *User ) err = r.Parse(&user) if err != nil { r.Response.WriteExit(err) } r.Response.WriteExit(user.Id, user.Name) }) }) 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", s.GetListenedPort())) t.Assert(c.GetContent(ctx, "/", ``), `The Id field is required`) t.Assert(c.GetContent(ctx, "/", `id=1&name=john`), `1john`) t.Assert(c.PostContent(ctx, "/", `id=1&name=john&password1=123&password2=456`), `1john`) t.Assert(c.PostContent(ctx, "/", `id=1`), `The name field is required`) }) } // https://github.com/gogf/gf/issues/1488 func Test_Params_Parse_Issue1488(t *testing.T) { s := g.Server(guid.S()) s.Group("/", func(group *ghttp.RouterGroup) { group.ALL("/", func(r *ghttp.Request) { type Request struct { Type []int `p:"type"` Keyword string `p:"keyword"` Limit int `p:"per_page" d:"10"` Page int `p:"page" d:"1"` Order string CreatedAtLte string CreatedAtGte string CreatorID []int } for i := 0; i < 10; i++ { r.SetParamMap(g.Map{ "type[]": 0, "keyword": "", "t_start": "", "t_end": "", "reserve_at_start": "", "reserve_at_end": "", "user_name": "", "flag": "", "per_page": 6, }) var parsed Request _ = r.Parse(&parsed) r.Response.Write(parsed.Page, parsed.Limit) } }) }) 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", s.GetListenedPort())) t.Assert(c.GetContent(ctx, "/", ``), `16161616161616161616`) }) }