github.com/gogf/gf@v1.16.9/net/ghttp/ghttp_unit_request_struct_test.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package ghttp_test
     8  
     9  import (
    10  	"fmt"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/gogf/gf/util/gvalid"
    15  
    16  	"github.com/gogf/gf/frame/g"
    17  	"github.com/gogf/gf/net/ghttp"
    18  	"github.com/gogf/gf/test/gtest"
    19  )
    20  
    21  func Test_Params_Parse(t *testing.T) {
    22  	type User struct {
    23  		Id   int
    24  		Name string
    25  		Map  map[string]interface{}
    26  	}
    27  	p, _ := ports.PopRand()
    28  	s := g.Server(p)
    29  	s.BindHandler("/parse", func(r *ghttp.Request) {
    30  		var user *User
    31  		if err := r.Parse(&user); err != nil {
    32  			r.Response.WriteExit(err)
    33  		}
    34  		r.Response.WriteExit(user.Map["id"], user.Map["score"])
    35  	})
    36  	s.SetPort(p)
    37  	s.SetDumpRouterMap(false)
    38  	s.Start()
    39  	defer s.Shutdown()
    40  
    41  	time.Sleep(100 * time.Millisecond)
    42  	gtest.C(t, func(t *gtest.T) {
    43  		client := g.Client()
    44  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
    45  		t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","map":{"id":1,"score":100}}`), `1100`)
    46  	})
    47  }
    48  
    49  func Test_Params_ParseQuery(t *testing.T) {
    50  	type User struct {
    51  		Id   int
    52  		Name string
    53  	}
    54  	p, _ := ports.PopRand()
    55  	s := g.Server(p)
    56  	s.BindHandler("/parse-query", func(r *ghttp.Request) {
    57  		var user *User
    58  		if err := r.ParseQuery(&user); err != nil {
    59  			r.Response.WriteExit(err)
    60  		}
    61  		r.Response.WriteExit(user.Id, user.Name)
    62  	})
    63  	s.SetPort(p)
    64  	s.SetDumpRouterMap(false)
    65  	s.Start()
    66  	defer s.Shutdown()
    67  
    68  	time.Sleep(100 * time.Millisecond)
    69  	gtest.C(t, func(t *gtest.T) {
    70  		c := g.Client()
    71  		c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
    72  		t.Assert(c.GetContent("/parse-query"), `0`)
    73  		t.Assert(c.GetContent("/parse-query?id=1&name=john"), `1john`)
    74  		t.Assert(c.PostContent("/parse-query"), `0`)
    75  		t.Assert(c.PostContent("/parse-query", g.Map{
    76  			"id":   1,
    77  			"name": "john",
    78  		}), `0`)
    79  	})
    80  }
    81  
    82  func Test_Params_ParseForm(t *testing.T) {
    83  	type User struct {
    84  		Id   int
    85  		Name string
    86  	}
    87  	p, _ := ports.PopRand()
    88  	s := g.Server(p)
    89  	s.BindHandler("/parse-form", func(r *ghttp.Request) {
    90  		var user *User
    91  		if err := r.ParseForm(&user); err != nil {
    92  			r.Response.WriteExit(err)
    93  		}
    94  		r.Response.WriteExit(user.Id, user.Name)
    95  	})
    96  	s.SetPort(p)
    97  	s.SetDumpRouterMap(false)
    98  	s.Start()
    99  	defer s.Shutdown()
   100  
   101  	time.Sleep(100 * time.Millisecond)
   102  	gtest.C(t, func(t *gtest.T) {
   103  		c := g.Client()
   104  		c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
   105  		t.Assert(c.GetContent("/parse-form"), `0`)
   106  		t.Assert(c.GetContent("/parse-form", g.Map{
   107  			"id":   1,
   108  			"name": "john",
   109  		}), 0)
   110  		t.Assert(c.PostContent("/parse-form"), `0`)
   111  		t.Assert(c.PostContent("/parse-form", g.Map{
   112  			"id":   1,
   113  			"name": "john",
   114  		}), `1john`)
   115  	})
   116  }
   117  
   118  func Test_Params_ComplexJsonStruct(t *testing.T) {
   119  	type ItemEnv struct {
   120  		Type  string
   121  		Key   string
   122  		Value string
   123  		Brief string
   124  	}
   125  
   126  	type ItemProbe struct {
   127  		Type           string
   128  		Port           int
   129  		Path           string
   130  		Brief          string
   131  		Period         int
   132  		InitialDelay   int
   133  		TimeoutSeconds int
   134  	}
   135  
   136  	type ItemKV struct {
   137  		Key   string
   138  		Value string
   139  	}
   140  
   141  	type ItemPort struct {
   142  		Port  int
   143  		Type  string
   144  		Alias string
   145  		Brief string
   146  	}
   147  
   148  	type ItemMount struct {
   149  		Type    string
   150  		DstPath string
   151  		Src     string
   152  		SrcPath string
   153  		Brief   string
   154  	}
   155  
   156  	type SaveRequest struct {
   157  		AppId          uint
   158  		Name           string
   159  		Type           string
   160  		Cluster        string
   161  		Replicas       uint
   162  		ContainerName  string
   163  		ContainerImage string
   164  		VersionTag     string
   165  		Namespace      string
   166  		Id             uint
   167  		Status         uint
   168  		Metrics        string
   169  		InitImage      string
   170  		CpuRequest     uint
   171  		CpuLimit       uint
   172  		MemRequest     uint
   173  		MemLimit       uint
   174  		MeshEnabled    uint
   175  		ContainerPorts []ItemPort
   176  		Labels         []ItemKV
   177  		NodeSelector   []ItemKV
   178  		EnvReserve     []ItemKV
   179  		EnvGlobal      []ItemEnv
   180  		EnvContainer   []ItemEnv
   181  		Mounts         []ItemMount
   182  		LivenessProbe  ItemProbe
   183  		ReadinessProbe ItemProbe
   184  	}
   185  
   186  	p, _ := ports.PopRand()
   187  	s := g.Server(p)
   188  	s.BindHandler("/parse", func(r *ghttp.Request) {
   189  		if m := r.GetMap(); len(m) > 0 {
   190  			var data *SaveRequest
   191  			if err := r.Parse(&data); err != nil {
   192  				r.Response.WriteExit(err)
   193  			}
   194  			r.Response.WriteExit(data)
   195  		}
   196  	})
   197  	s.SetPort(p)
   198  	s.SetDumpRouterMap(false)
   199  	s.Start()
   200  	defer s.Shutdown()
   201  
   202  	time.Sleep(100 * time.Millisecond)
   203  	gtest.C(t, func(t *gtest.T) {
   204  		client := g.Client()
   205  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
   206  		content := `
   207  {
   208      "app_id": 5,
   209      "cluster": "test",
   210      "container_image": "nginx",
   211      "container_name": "test",
   212      "container_ports": [
   213          {
   214              "alias": "别名",
   215              "brief": "描述",
   216              "port": 80,
   217              "type": "tcp"
   218          }
   219      ],
   220      "cpu_limit": 100,
   221      "cpu_request": 10,
   222      "create_at": "2020-10-10 12:00:00",
   223      "creator": 1,
   224      "env_container": [
   225          {
   226              "brief": "用户环境变量",
   227              "key": "NAME",
   228              "type": "string",
   229              "value": "john"
   230          }
   231      ],
   232      "env_global": [
   233          {
   234              "brief": "数据数量",
   235              "key": "NUMBER",
   236              "type": "string",
   237              "value": "1"
   238          }
   239      ],
   240      "env_reserve": [
   241          {
   242              "key": "NODE_IP",
   243              "value": "status.hostIP"
   244          }
   245      ],
   246      "liveness_probe": {
   247          "brief": "存活探针",
   248          "initial_delay": 10,
   249          "path": "",
   250          "period": 5,
   251          "port": 80,
   252          "type": "tcpSocket"
   253      },
   254      "readiness_probe": {
   255          "brief": "就绪探针",
   256          "initial_delay": 10,
   257          "path": "",
   258          "period": 5,
   259          "port": 80,
   260          "type": "tcpSocket"
   261      },
   262      "id": 0,
   263      "init_image": "",
   264      "labels": [
   265          {
   266              "key": "app",
   267              "value": "test"
   268          }
   269      ],
   270      "mem_limit": 1000,
   271      "mem_request": 100,
   272      "mesh_enabled": 0,
   273      "metrics": "",
   274      "mounts": [],
   275      "name": "test",
   276      "namespace": "test",
   277      "node_selector": [
   278          {
   279              "key": "group",
   280              "value": "app"
   281          }
   282      ],
   283      "replicas": 1,
   284      "type": "test",
   285      "update_at": "2020-10-10 12:00:00",
   286      "version_tag": "test"
   287  }
   288  `
   289  		t.Assert(client.PostContent("/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}}`)
   290  	})
   291  }
   292  
   293  func Test_Params_Parse_Attr_Pointer1(t *testing.T) {
   294  	type User struct {
   295  		Id   *int
   296  		Name *string
   297  	}
   298  	p, _ := ports.PopRand()
   299  	s := g.Server(p)
   300  	s.BindHandler("/parse1", func(r *ghttp.Request) {
   301  		if m := r.GetMap(); len(m) > 0 {
   302  			var user *User
   303  			if err := r.Parse(&user); err != nil {
   304  				r.Response.WriteExit(err)
   305  			}
   306  			r.Response.WriteExit(user.Id, user.Name)
   307  		}
   308  	})
   309  	s.BindHandler("/parse2", func(r *ghttp.Request) {
   310  		if m := r.GetMap(); len(m) > 0 {
   311  			var user = new(User)
   312  			if err := r.Parse(user); err != nil {
   313  				r.Response.WriteExit(err)
   314  			}
   315  			r.Response.WriteExit(user.Id, user.Name)
   316  		}
   317  	})
   318  	s.SetPort(p)
   319  	s.SetDumpRouterMap(false)
   320  	s.Start()
   321  	defer s.Shutdown()
   322  
   323  	time.Sleep(100 * time.Millisecond)
   324  	gtest.C(t, func(t *gtest.T) {
   325  		client := g.Client()
   326  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
   327  		t.Assert(client.PostContent("/parse1", `{"id":1,"name":"john"}`), `1john`)
   328  		t.Assert(client.PostContent("/parse2", `{"id":1,"name":"john"}`), `1john`)
   329  		t.Assert(client.PostContent("/parse2?id=1&name=john"), `1john`)
   330  		t.Assert(client.PostContent("/parse2", `id=1&name=john`), `1john`)
   331  	})
   332  }
   333  
   334  func Test_Params_Parse_Attr_Pointer2(t *testing.T) {
   335  	type User struct {
   336  		Id *int `v:"required"`
   337  	}
   338  	p, _ := ports.PopRand()
   339  	s := g.Server(p)
   340  	s.BindHandler("/parse", func(r *ghttp.Request) {
   341  		var user *User
   342  		if err := r.Parse(&user); err != nil {
   343  			r.Response.WriteExit(err.Error())
   344  		}
   345  		r.Response.WriteExit(user.Id)
   346  	})
   347  	s.SetPort(p)
   348  	s.SetDumpRouterMap(false)
   349  	s.Start()
   350  	defer s.Shutdown()
   351  
   352  	time.Sleep(100 * time.Millisecond)
   353  	gtest.C(t, func(t *gtest.T) {
   354  		client := g.Client()
   355  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
   356  		t.Assert(client.PostContent("/parse"), `The Id field is required`)
   357  		t.Assert(client.PostContent("/parse?id=1"), `1`)
   358  	})
   359  }
   360  
   361  // It does not support this kind of converting yet.
   362  //func Test_Params_Parse_Attr_SliceSlice(t *testing.T) {
   363  //	type User struct {
   364  //		Id     int
   365  //		Name   string
   366  //		Scores [][]int
   367  //	}
   368  //	p, _ := ports.PopRand()
   369  //	s := g.Server(p)
   370  //	s.BindHandler("/parse", func(r *ghttp.Request) {
   371  //		if m := r.GetMap(); len(m) > 0 {
   372  //			var user *User
   373  //			if err := r.Parse(&user); err != nil {
   374  //				r.Response.WriteExit(err)
   375  //			}
   376  //			r.Response.WriteExit(user.Scores)
   377  //		}
   378  //	})
   379  //	s.SetPort(p)
   380  //	s.SetDumpRouterMap(false)
   381  //	s.Start()
   382  //	defer s.Shutdown()
   383  //
   384  //	time.Sleep(100 * time.Millisecond)
   385  //	gtest.C(t, func(t *gtest.T) {
   386  //		client := g.Client()
   387  //		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
   388  //		t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","scores":[[1,2,3]]}`), `1100`)
   389  //	})
   390  //}
   391  
   392  func Test_Params_Struct(t *testing.T) {
   393  	type User struct {
   394  		Id    int
   395  		Name  string
   396  		Time  *time.Time
   397  		Pass1 string `p:"password1"`
   398  		Pass2 string `p:"password2" v:"password2 @required|length:2,20|password3#||密码强度不足"`
   399  	}
   400  	p, _ := ports.PopRand()
   401  	s := g.Server(p)
   402  	s.BindHandler("/struct1", func(r *ghttp.Request) {
   403  		if m := r.GetMap(); len(m) > 0 {
   404  			user := new(User)
   405  			if err := r.GetStruct(user); err != nil {
   406  				r.Response.WriteExit(err)
   407  			}
   408  			r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2)
   409  		}
   410  	})
   411  	s.BindHandler("/struct2", func(r *ghttp.Request) {
   412  		if m := r.GetMap(); len(m) > 0 {
   413  			user := (*User)(nil)
   414  			if err := r.GetStruct(&user); err != nil {
   415  				r.Response.WriteExit(err)
   416  			}
   417  			if user != nil {
   418  				r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2)
   419  			}
   420  		}
   421  	})
   422  	s.BindHandler("/struct-valid", func(r *ghttp.Request) {
   423  		if m := r.GetMap(); len(m) > 0 {
   424  			user := new(User)
   425  			if err := r.GetStruct(user); err != nil {
   426  				r.Response.WriteExit(err)
   427  			}
   428  			if err := gvalid.CheckStruct(r.Context(), user, nil); err != nil {
   429  				r.Response.WriteExit(err)
   430  			}
   431  		}
   432  	})
   433  	s.BindHandler("/parse", func(r *ghttp.Request) {
   434  		if m := r.GetMap(); len(m) > 0 {
   435  			var user *User
   436  			if err := r.Parse(&user); err != nil {
   437  				r.Response.WriteExit(err)
   438  			}
   439  			r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2)
   440  		}
   441  	})
   442  	s.SetPort(p)
   443  	s.SetDumpRouterMap(false)
   444  	s.Start()
   445  	defer s.Shutdown()
   446  
   447  	time.Sleep(100 * time.Millisecond)
   448  	gtest.C(t, func(t *gtest.T) {
   449  		client := g.Client()
   450  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
   451  		t.Assert(client.GetContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`)
   452  		t.Assert(client.PostContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`)
   453  		t.Assert(client.PostContent("/struct2", `id=1&name=john&password1=123&password2=456`), `1john123456`)
   454  		t.Assert(client.PostContent("/struct2", ``), ``)
   455  		t.Assert(client.PostContent("/struct-valid", `id=1&name=john&password1=123&password2=0`), `The password2 value length must be between 2 and 20; 密码强度不足`)
   456  		t.Assert(client.PostContent("/parse", `id=1&name=john&password1=123&password2=0`), `The password2 value length must be between 2 and 20; 密码强度不足`)
   457  		t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john123Abc!@#123Abc!@#`)
   458  	})
   459  }
   460  
   461  func Test_Params_Structs(t *testing.T) {
   462  	type User struct {
   463  		Id    int
   464  		Name  string
   465  		Time  *time.Time
   466  		Pass1 string `p:"password1"`
   467  		Pass2 string `p:"password2" v:"password2 @required|length:2,20|password3#||密码强度不足"`
   468  	}
   469  	p, _ := ports.PopRand()
   470  	s := g.Server(p)
   471  	s.BindHandler("/parse1", func(r *ghttp.Request) {
   472  		var users []*User
   473  		if err := r.Parse(&users); err != nil {
   474  			r.Response.WriteExit(err)
   475  		}
   476  		r.Response.WriteExit(users[0].Id, users[1].Id)
   477  	})
   478  	s.SetPort(p)
   479  	s.SetDumpRouterMap(false)
   480  	s.Start()
   481  	defer s.Shutdown()
   482  
   483  	time.Sleep(100 * time.Millisecond)
   484  	gtest.C(t, func(t *gtest.T) {
   485  		client := g.Client()
   486  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
   487  		t.Assert(client.PostContent(
   488  			"/parse1",
   489  			`[{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}, {"id":2,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}]`),
   490  			`12`,
   491  		)
   492  	})
   493  }
   494  
   495  func Test_Params_Struct_Validation(t *testing.T) {
   496  	type User struct {
   497  		Id   int    `v:"required"`
   498  		Name string `v:"name@required-with:id"`
   499  	}
   500  	p, _ := ports.PopRand()
   501  	s := g.Server(p)
   502  	s.Group("/", func(group *ghttp.RouterGroup) {
   503  		group.ALL("/", func(r *ghttp.Request) {
   504  			var (
   505  				err  error
   506  				user *User
   507  			)
   508  			err = r.Parse(&user)
   509  			if err != nil {
   510  				r.Response.WriteExit(err)
   511  			}
   512  			r.Response.WriteExit(user.Id, user.Name)
   513  		})
   514  	})
   515  	s.SetPort(p)
   516  	s.SetDumpRouterMap(false)
   517  	s.Start()
   518  	defer s.Shutdown()
   519  
   520  	time.Sleep(100 * time.Millisecond)
   521  	gtest.C(t, func(t *gtest.T) {
   522  		c := g.Client()
   523  		c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
   524  		t.Assert(c.GetContent("/", ``), `The Id field is required`)
   525  		t.Assert(c.GetContent("/", `id=1&name=john`), `1john`)
   526  		t.Assert(c.PostContent("/", `id=1&name=john&password1=123&password2=456`), `1john`)
   527  		t.Assert(c.PostContent("/", `id=1`), `The name field is required`)
   528  	})
   529  }