github.com/gogf/gf/v2@v2.7.4/net/ghttp/ghttp_z_unit_feature_router_strict_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  	"context"
    11  	"fmt"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/gogf/gf/v2/encoding/gjson"
    16  	"github.com/gogf/gf/v2/errors/gerror"
    17  	"github.com/gogf/gf/v2/frame/g"
    18  	"github.com/gogf/gf/v2/internal/json"
    19  	"github.com/gogf/gf/v2/net/ghttp"
    20  	"github.com/gogf/gf/v2/test/gtest"
    21  	"github.com/gogf/gf/v2/util/guid"
    22  )
    23  
    24  func Test_Router_Handler_Strict_WithObject(t *testing.T) {
    25  	type TestReq struct {
    26  		Age  int
    27  		Name string
    28  	}
    29  	type TestRes struct {
    30  		Id   int
    31  		Age  int
    32  		Name string
    33  	}
    34  	s := g.Server(guid.S())
    35  	s.Use(ghttp.MiddlewareHandlerResponse)
    36  	s.BindHandler("/test", func(ctx context.Context, req *TestReq) (res *TestRes, err error) {
    37  		return &TestRes{
    38  			Id:   1,
    39  			Age:  req.Age,
    40  			Name: req.Name,
    41  		}, nil
    42  	})
    43  	s.BindHandler("/test/error", func(ctx context.Context, req *TestReq) (res *TestRes, err error) {
    44  		return &TestRes{
    45  			Id:   1,
    46  			Age:  req.Age,
    47  			Name: req.Name,
    48  		}, gerror.New("error")
    49  	})
    50  	s.SetDumpRouterMap(false)
    51  	s.Start()
    52  	defer s.Shutdown()
    53  
    54  	time.Sleep(100 * time.Millisecond)
    55  	gtest.C(t, func(t *gtest.T) {
    56  		client := g.Client()
    57  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
    58  
    59  		t.Assert(client.GetContent(ctx, "/test?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18,"Name":"john"}}`)
    60  		t.Assert(client.GetContent(ctx, "/test/error"), `{"code":50,"message":"error","data":{"Id":1,"Age":0,"Name":""}}`)
    61  	})
    62  }
    63  
    64  type TestForHandlerWithObjectAndMeta1Req struct {
    65  	g.Meta `path:"/custom-test1" method:"get"`
    66  	Age    int
    67  	Name   string
    68  }
    69  
    70  type TestForHandlerWithObjectAndMeta1Res struct {
    71  	Id  int
    72  	Age int
    73  }
    74  
    75  type TestForHandlerWithObjectAndMeta2Req struct {
    76  	g.Meta `path:"/custom-test2" method:"get"`
    77  	Age    int
    78  	Name   string
    79  }
    80  
    81  type TestForHandlerWithObjectAndMeta2Res struct {
    82  	Id   int
    83  	Name string
    84  }
    85  
    86  type ControllerForHandlerWithObjectAndMeta1 struct{}
    87  
    88  func (ControllerForHandlerWithObjectAndMeta1) Index(ctx context.Context, req *TestForHandlerWithObjectAndMeta1Req) (res *TestForHandlerWithObjectAndMeta1Res, err error) {
    89  	return &TestForHandlerWithObjectAndMeta1Res{
    90  		Id:  1,
    91  		Age: req.Age,
    92  	}, nil
    93  }
    94  
    95  func (ControllerForHandlerWithObjectAndMeta1) Test2(ctx context.Context, req *TestForHandlerWithObjectAndMeta2Req) (res *TestForHandlerWithObjectAndMeta2Res, err error) {
    96  	return &TestForHandlerWithObjectAndMeta2Res{
    97  		Id:   1,
    98  		Name: req.Name,
    99  	}, nil
   100  }
   101  
   102  type TestForHandlerWithObjectAndMeta3Req struct {
   103  	g.Meta `path:"/custom-test3" method:"get"`
   104  	Age    int
   105  	Name   string
   106  }
   107  
   108  type TestForHandlerWithObjectAndMeta3Res struct {
   109  	Id  int
   110  	Age int
   111  }
   112  
   113  type TestForHandlerWithObjectAndMeta4Req struct {
   114  	g.Meta `path:"/custom-test4" method:"get"`
   115  	Age    int
   116  	Name   string
   117  }
   118  
   119  type TestForHandlerWithObjectAndMeta4Res struct {
   120  	Id   int
   121  	Name string
   122  }
   123  
   124  type ControllerForHandlerWithObjectAndMeta2 struct{}
   125  
   126  func (ControllerForHandlerWithObjectAndMeta2) Test3(ctx context.Context, req *TestForHandlerWithObjectAndMeta3Req) (res *TestForHandlerWithObjectAndMeta3Res, err error) {
   127  	return &TestForHandlerWithObjectAndMeta3Res{
   128  		Id:  1,
   129  		Age: req.Age,
   130  	}, nil
   131  }
   132  
   133  func (ControllerForHandlerWithObjectAndMeta2) Test4(ctx context.Context, req *TestForHandlerWithObjectAndMeta4Req) (res *TestForHandlerWithObjectAndMeta4Res, err error) {
   134  	return &TestForHandlerWithObjectAndMeta4Res{
   135  		Id:   1,
   136  		Name: req.Name,
   137  	}, nil
   138  }
   139  
   140  func Test_Router_Handler_Strict_WithObjectAndMeta(t *testing.T) {
   141  	s := g.Server(guid.S())
   142  	s.Use(ghttp.MiddlewareHandlerResponse)
   143  	s.Group("/", func(group *ghttp.RouterGroup) {
   144  		group.ALL("/", new(ControllerForHandlerWithObjectAndMeta1))
   145  	})
   146  	s.SetDumpRouterMap(false)
   147  	s.Start()
   148  	defer s.Shutdown()
   149  
   150  	time.Sleep(100 * time.Millisecond)
   151  	gtest.C(t, func(t *gtest.T) {
   152  		client := g.Client()
   153  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   154  
   155  		t.Assert(client.GetContent(ctx, "/"), `{"code":65,"message":"Not Found","data":null}`)
   156  		t.Assert(client.GetContent(ctx, "/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
   157  		t.Assert(client.GetContent(ctx, "/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
   158  		t.Assert(client.PostContent(ctx, "/custom-test2?age=18&name=john"), `{"code":65,"message":"Not Found","data":null}`)
   159  	})
   160  }
   161  
   162  func Test_Router_Handler_Strict_Group_Bind(t *testing.T) {
   163  	s := g.Server(guid.S())
   164  	s.Use(ghttp.MiddlewareHandlerResponse)
   165  	s.Group("/api/v1", func(group *ghttp.RouterGroup) {
   166  		group.Bind(
   167  			new(ControllerForHandlerWithObjectAndMeta1),
   168  			new(ControllerForHandlerWithObjectAndMeta2),
   169  		)
   170  	})
   171  	s.Group("/api/v2", func(group *ghttp.RouterGroup) {
   172  		group.Bind(
   173  			new(ControllerForHandlerWithObjectAndMeta1),
   174  			new(ControllerForHandlerWithObjectAndMeta2),
   175  		)
   176  	})
   177  	s.SetDumpRouterMap(false)
   178  	s.Start()
   179  	defer s.Shutdown()
   180  
   181  	time.Sleep(100 * time.Millisecond)
   182  	gtest.C(t, func(t *gtest.T) {
   183  		client := g.Client()
   184  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   185  
   186  		t.Assert(client.GetContent(ctx, "/"), `{"code":65,"message":"Not Found","data":null}`)
   187  		t.Assert(client.GetContent(ctx, "/api/v1/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
   188  		t.Assert(client.GetContent(ctx, "/api/v1/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
   189  		t.Assert(client.PostContent(ctx, "/api/v1/custom-test2?age=18&name=john"), `{"code":65,"message":"Not Found","data":null}`)
   190  
   191  		t.Assert(client.GetContent(ctx, "/api/v1/custom-test3?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
   192  		t.Assert(client.GetContent(ctx, "/api/v1/custom-test4?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
   193  
   194  		t.Assert(client.GetContent(ctx, "/api/v2/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
   195  		t.Assert(client.GetContent(ctx, "/api/v2/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
   196  		t.Assert(client.GetContent(ctx, "/api/v2/custom-test3?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
   197  		t.Assert(client.GetContent(ctx, "/api/v2/custom-test4?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
   198  	})
   199  }
   200  
   201  func Test_Issue1708(t *testing.T) {
   202  	type Test struct {
   203  		Name string `json:"name"`
   204  	}
   205  	type Req struct {
   206  		Page       int      `json:"page"       dc:"分页码"`
   207  		Size       int      `json:"size"       dc:"分页数量"`
   208  		TargetType string   `json:"targetType" v:"required#评论内容类型错误" dc:"评论类型: topic/ask/article/reply"`
   209  		TargetId   uint     `json:"targetId"   v:"required#评论目标ID错误" dc:"对应内容ID"`
   210  		Test       [][]Test `json:"test"`
   211  	}
   212  	type Res struct {
   213  		Page       int      `json:"page"       dc:"分页码"`
   214  		Size       int      `json:"size"       dc:"分页数量"`
   215  		TargetType string   `json:"targetType" v:"required#评论内容类型错误" dc:"评论类型: topic/ask/article/reply"`
   216  		TargetId   uint     `json:"targetId"   v:"required#评论目标ID错误" dc:"对应内容ID"`
   217  		Test       [][]Test `json:"test"`
   218  	}
   219  
   220  	s := g.Server(guid.S())
   221  	s.Use(ghttp.MiddlewareHandlerResponse)
   222  	s.BindHandler("/test", func(ctx context.Context, req *Req) (res *Res, err error) {
   223  		return &Res{
   224  			Page:       req.Page,
   225  			Size:       req.Size,
   226  			TargetType: req.TargetType,
   227  			TargetId:   req.TargetId,
   228  			Test:       req.Test,
   229  		}, nil
   230  	})
   231  	s.SetDumpRouterMap(false)
   232  	s.Start()
   233  	defer s.Shutdown()
   234  
   235  	time.Sleep(100 * time.Millisecond)
   236  	gtest.C(t, func(t *gtest.T) {
   237  		client := g.Client()
   238  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   239  		content := `
   240  {
   241      "targetType":"topic",
   242      "targetId":10785,
   243      "test":[
   244          [
   245              {
   246                  "name":"123"
   247              }
   248          ]
   249      ]
   250  }
   251  `
   252  		t.Assert(
   253  			client.PostContent(ctx, "/test", content),
   254  			`{"code":0,"message":"","data":{"page":0,"size":0,"targetType":"topic","targetId":10785,"test":[[{"name":"123"}]]}}`,
   255  		)
   256  	})
   257  }
   258  
   259  func Test_Custom_Slice_Type_Attribute(t *testing.T) {
   260  	type (
   261  		WhiteListKey    string
   262  		WhiteListValues []string
   263  		WhiteList       map[WhiteListKey]WhiteListValues
   264  	)
   265  	type Req struct {
   266  		Id   int
   267  		List WhiteList
   268  	}
   269  	type Res struct {
   270  		Content string
   271  	}
   272  
   273  	s := g.Server(guid.S())
   274  	s.Use(ghttp.MiddlewareHandlerResponse)
   275  	s.BindHandler("/test", func(ctx context.Context, req *Req) (res *Res, err error) {
   276  		return &Res{
   277  			Content: gjson.MustEncodeString(req),
   278  		}, nil
   279  	})
   280  	s.SetDumpRouterMap(false)
   281  	s.Start()
   282  	defer s.Shutdown()
   283  
   284  	time.Sleep(100 * time.Millisecond)
   285  	gtest.C(t, func(t *gtest.T) {
   286  		client := g.Client()
   287  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   288  		content := `
   289  {
   290      "id":1,
   291  	"list":{
   292  		"key": ["a", "b", "c"]
   293  	}
   294  }
   295  `
   296  		t.Assert(
   297  			client.PostContent(ctx, "/test", content),
   298  			`{"code":0,"message":"","data":{"Content":"{\"Id\":1,\"List\":{\"key\":[\"a\",\"b\",\"c\"]}}"}}`,
   299  		)
   300  	})
   301  }
   302  
   303  func Test_Router_Handler_Strict_WithGeneric(t *testing.T) {
   304  	type TestReq struct {
   305  		Age int
   306  	}
   307  	type TestGeneric[T any] struct {
   308  		Test T
   309  	}
   310  	type Test1Res struct {
   311  		Age TestGeneric[int]
   312  	}
   313  	type Test2Res TestGeneric[int]
   314  	type TestGenericRes[T any] struct {
   315  		Test T
   316  	}
   317  
   318  	s := g.Server(guid.S())
   319  	s.Use(ghttp.MiddlewareHandlerResponse)
   320  	s.BindHandler("/test1", func(ctx context.Context, req *TestReq) (res *Test1Res, err error) {
   321  		return &Test1Res{
   322  			Age: TestGeneric[int]{
   323  				Test: req.Age,
   324  			},
   325  		}, nil
   326  	})
   327  	s.BindHandler("/test1_slice", func(ctx context.Context, req *TestReq) (res []Test1Res, err error) {
   328  		return []Test1Res{
   329  			Test1Res{
   330  				Age: TestGeneric[int]{
   331  					Test: req.Age,
   332  				},
   333  			},
   334  		}, nil
   335  	})
   336  	s.BindHandler("/test2", func(ctx context.Context, req *TestReq) (res *Test2Res, err error) {
   337  		return &Test2Res{
   338  			Test: req.Age,
   339  		}, nil
   340  	})
   341  
   342  	s.BindHandler("/test2_slice", func(ctx context.Context, req *TestReq) (res []Test2Res, err error) {
   343  		return []Test2Res{
   344  			Test2Res{
   345  				Test: req.Age,
   346  			},
   347  		}, nil
   348  	})
   349  
   350  	s.BindHandler("/test3", func(ctx context.Context, req *TestReq) (res *TestGenericRes[int], err error) {
   351  		return &TestGenericRes[int]{
   352  			Test: req.Age,
   353  		}, nil
   354  	})
   355  
   356  	s.SetDumpRouterMap(false)
   357  	s.Start()
   358  	defer s.Shutdown()
   359  
   360  	s.BindHandler("/test3_slice", func(ctx context.Context, req *TestReq) (res []TestGenericRes[int], err error) {
   361  		return []TestGenericRes[int]{
   362  			TestGenericRes[int]{
   363  				Test: req.Age,
   364  			},
   365  		}, nil
   366  	})
   367  
   368  	time.Sleep(100 * time.Millisecond)
   369  	gtest.C(t, func(t *gtest.T) {
   370  		client := g.Client()
   371  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   372  
   373  		t.Assert(client.GetContent(ctx, "/test1?age=1"), `{"code":0,"message":"","data":{"Age":{"Test":1}}}`)
   374  		t.Assert(client.GetContent(ctx, "/test1_slice?age=1"), `{"code":0,"message":"","data":[{"Age":{"Test":1}}]}`)
   375  		t.Assert(client.GetContent(ctx, "/test2?age=2"), `{"code":0,"message":"","data":{"Test":2}}`)
   376  		t.Assert(client.GetContent(ctx, "/test2_slice?age=2"), `{"code":0,"message":"","data":[{"Test":2}]}`)
   377  		t.Assert(client.GetContent(ctx, "/test3?age=3"), `{"code":0,"message":"","data":{"Test":3}}`)
   378  		t.Assert(client.GetContent(ctx, "/test3_slice?age=3"), `{"code":0,"message":"","data":[{"Test":3}]}`)
   379  	})
   380  }
   381  
   382  type ParameterCaseSensitiveController struct{}
   383  
   384  type ParameterCaseSensitiveControllerPathReq struct {
   385  	g.Meta `path:"/api/*path" method:"post"`
   386  	Path   string
   387  }
   388  
   389  type ParameterCaseSensitiveControllerPathRes struct {
   390  	Path string
   391  }
   392  
   393  func (c *ParameterCaseSensitiveController) Path(
   394  	ctx context.Context,
   395  	req *ParameterCaseSensitiveControllerPathReq,
   396  ) (res *ParameterCaseSensitiveControllerPathRes, err error) {
   397  	return &ParameterCaseSensitiveControllerPathRes{Path: req.Path}, nil
   398  }
   399  
   400  func Test_Router_Handler_Strict_ParameterCaseSensitive(t *testing.T) {
   401  	s := g.Server(guid.S())
   402  	s.Use(ghttp.MiddlewareHandlerResponse)
   403  	s.Group("/", func(group *ghttp.RouterGroup) {
   404  		group.Bind(&ParameterCaseSensitiveController{})
   405  	})
   406  	s.SetDumpRouterMap(false)
   407  	s.Start()
   408  	defer s.Shutdown()
   409  
   410  	time.Sleep(100 * time.Millisecond)
   411  	gtest.C(t, func(t *gtest.T) {
   412  		client := g.Client()
   413  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   414  
   415  		for i := 0; i < 1000; i++ {
   416  			t.Assert(
   417  				client.PostContent(ctx, "/api/111", `{"Path":"222"}`),
   418  				`{"code":0,"message":"","data":{"Path":"222"}}`,
   419  			)
   420  		}
   421  	})
   422  }
   423  
   424  type testJsonRawMessageIssue3449Req struct {
   425  	g.Meta `path:"/test" method:"POST" sm:"hello" tags:"示例"`
   426  
   427  	Name    string          `json:"name" v:"required" dc:"名称"`
   428  	JSONRaw json.RawMessage `json:"jsonRaw" dc:"原始JSON"`
   429  }
   430  type testJsonRawMessageIssue3449Res struct {
   431  	Name    string          `json:"name" v:"required" dc:"名称"`
   432  	JSONRaw json.RawMessage `json:"jsonRaw" dc:"原始JSON"`
   433  }
   434  
   435  type testJsonRawMessageIssue3449 struct {
   436  }
   437  
   438  func (t *testJsonRawMessageIssue3449) Test(ctx context.Context, req *testJsonRawMessageIssue3449Req) (res *testJsonRawMessageIssue3449Res, err error) {
   439  	return &testJsonRawMessageIssue3449Res{
   440  		Name:    req.Name,
   441  		JSONRaw: req.JSONRaw,
   442  	}, nil
   443  }
   444  
   445  // https://github.com/gogf/gf/issues/3449
   446  func Test_JsonRawMessage_Issue3449(t *testing.T) {
   447  
   448  	s := g.Server(guid.S())
   449  	s.Use(ghttp.MiddlewareHandlerResponse)
   450  	s.Group("/", func(group *ghttp.RouterGroup) {
   451  		group.Bind(new(testJsonRawMessageIssue3449))
   452  	})
   453  	s.SetDumpRouterMap(false)
   454  	s.Start()
   455  	defer s.Shutdown()
   456  
   457  	time.Sleep(100 * time.Millisecond)
   458  	gtest.C(t, func(t *gtest.T) {
   459  		client := g.Client()
   460  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   461  		v1 := map[string]any{
   462  			"jkey1": "11",
   463  			"jkey2": "12",
   464  		}
   465  
   466  		v2 := map[string]any{
   467  			"jkey1": "21",
   468  			"jkey2": "22",
   469  		}
   470  		data := map[string]any{
   471  			"Name": "test",
   472  			"jsonRaw": []any{
   473  				v1, v2,
   474  			},
   475  		}
   476  
   477  		expect1 := `{"code":0,"message":"","data":{"name":"test","jsonRaw":[{"jkey1":"11","jkey2":"12"},{"jkey1":"21","jkey2":"22"}]}}`
   478  		t.Assert(client.PostContent(ctx, "/test", data), expect1)
   479  
   480  		expect2 := `{"code":0,"message":"","data":{"name":"test","jsonRaw":{"jkey1":"11","jkey2":"12"}}}`
   481  		t.Assert(client.PostContent(ctx, "/test", map[string]any{
   482  			"Name":    "test",
   483  			"jsonRaw": v1,
   484  		}), expect2)
   485  
   486  	})
   487  }
   488  
   489  type testNullStringIssue3465Req struct {
   490  	g.Meta `path:"/test" method:"get" sm:"hello" tags:"示例"`
   491  	Name   []string `json:"name" v:"required"`
   492  }
   493  type testNullStringIssue3465Res struct {
   494  	Name []string `json:"name" v:"required" `
   495  }
   496  
   497  type testNullStringIssue3465 struct {
   498  }
   499  
   500  func (t *testNullStringIssue3465) Test(ctx context.Context, req *testNullStringIssue3465Req) (res *testNullStringIssue3465Res, err error) {
   501  	return &testNullStringIssue3465Res{
   502  		Name: req.Name,
   503  	}, nil
   504  }
   505  
   506  // https://github.com/gogf/gf/issues/3465
   507  func Test_NullString_Issue3465(t *testing.T) {
   508  
   509  	s := g.Server(guid.S())
   510  	s.Use(ghttp.MiddlewareHandlerResponse)
   511  	s.Group("/", func(group *ghttp.RouterGroup) {
   512  		group.Bind(new(testNullStringIssue3465))
   513  	})
   514  	s.SetDumpRouterMap(false)
   515  	s.Start()
   516  	defer s.Shutdown()
   517  
   518  	time.Sleep(100 * time.Millisecond)
   519  	gtest.C(t, func(t *gtest.T) {
   520  		client := g.Client()
   521  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   522  
   523  		data1 := map[string]any{
   524  			"name": "null",
   525  		}
   526  
   527  		expect1 := `{"code":0,"message":"","data":{"name":["null"]}}`
   528  		t.Assert(client.GetContent(ctx, "/test", data1), expect1)
   529  
   530  		data2 := map[string]any{
   531  			"name": []string{"null", "null"},
   532  		}
   533  		expect2 := `{"code":0,"message":"","data":{"name":["null","null"]}}`
   534  		t.Assert(client.GetContent(ctx, "/test", data2), expect2)
   535  
   536  	})
   537  }