github.com/wangyougui/gf/v2@v2.6.5/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/wangyougui/gf.
     6  
     7  package ghttp_test
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/wangyougui/gf/v2/encoding/gjson"
    16  	"github.com/wangyougui/gf/v2/errors/gerror"
    17  	"github.com/wangyougui/gf/v2/frame/g"
    18  	"github.com/wangyougui/gf/v2/net/ghttp"
    19  	"github.com/wangyougui/gf/v2/test/gtest"
    20  	"github.com/wangyougui/gf/v2/util/guid"
    21  )
    22  
    23  func Test_Router_Handler_Strict_WithObject(t *testing.T) {
    24  	type TestReq struct {
    25  		Age  int
    26  		Name string
    27  	}
    28  	type TestRes struct {
    29  		Id   int
    30  		Age  int
    31  		Name string
    32  	}
    33  	s := g.Server(guid.S())
    34  	s.Use(ghttp.MiddlewareHandlerResponse)
    35  	s.BindHandler("/test", func(ctx context.Context, req *TestReq) (res *TestRes, err error) {
    36  		return &TestRes{
    37  			Id:   1,
    38  			Age:  req.Age,
    39  			Name: req.Name,
    40  		}, nil
    41  	})
    42  	s.BindHandler("/test/error", func(ctx context.Context, req *TestReq) (res *TestRes, err error) {
    43  		return &TestRes{
    44  			Id:   1,
    45  			Age:  req.Age,
    46  			Name: req.Name,
    47  		}, gerror.New("error")
    48  	})
    49  	s.SetDumpRouterMap(false)
    50  	s.Start()
    51  	defer s.Shutdown()
    52  
    53  	time.Sleep(100 * time.Millisecond)
    54  	gtest.C(t, func(t *gtest.T) {
    55  		client := g.Client()
    56  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
    57  
    58  		t.Assert(client.GetContent(ctx, "/test?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18,"Name":"john"}}`)
    59  		t.Assert(client.GetContent(ctx, "/test/error"), `{"code":50,"message":"error","data":{"Id":1,"Age":0,"Name":""}}`)
    60  	})
    61  }
    62  
    63  type TestForHandlerWithObjectAndMeta1Req struct {
    64  	g.Meta `path:"/custom-test1" method:"get"`
    65  	Age    int
    66  	Name   string
    67  }
    68  
    69  type TestForHandlerWithObjectAndMeta1Res struct {
    70  	Id  int
    71  	Age int
    72  }
    73  
    74  type TestForHandlerWithObjectAndMeta2Req struct {
    75  	g.Meta `path:"/custom-test2" method:"get"`
    76  	Age    int
    77  	Name   string
    78  }
    79  
    80  type TestForHandlerWithObjectAndMeta2Res struct {
    81  	Id   int
    82  	Name string
    83  }
    84  
    85  type ControllerForHandlerWithObjectAndMeta1 struct{}
    86  
    87  func (ControllerForHandlerWithObjectAndMeta1) Index(ctx context.Context, req *TestForHandlerWithObjectAndMeta1Req) (res *TestForHandlerWithObjectAndMeta1Res, err error) {
    88  	return &TestForHandlerWithObjectAndMeta1Res{
    89  		Id:  1,
    90  		Age: req.Age,
    91  	}, nil
    92  }
    93  
    94  func (ControllerForHandlerWithObjectAndMeta1) Test2(ctx context.Context, req *TestForHandlerWithObjectAndMeta2Req) (res *TestForHandlerWithObjectAndMeta2Res, err error) {
    95  	return &TestForHandlerWithObjectAndMeta2Res{
    96  		Id:   1,
    97  		Name: req.Name,
    98  	}, nil
    99  }
   100  
   101  type TestForHandlerWithObjectAndMeta3Req struct {
   102  	g.Meta `path:"/custom-test3" method:"get"`
   103  	Age    int
   104  	Name   string
   105  }
   106  
   107  type TestForHandlerWithObjectAndMeta3Res struct {
   108  	Id  int
   109  	Age int
   110  }
   111  
   112  type TestForHandlerWithObjectAndMeta4Req struct {
   113  	g.Meta `path:"/custom-test4" method:"get"`
   114  	Age    int
   115  	Name   string
   116  }
   117  
   118  type TestForHandlerWithObjectAndMeta4Res struct {
   119  	Id   int
   120  	Name string
   121  }
   122  
   123  type ControllerForHandlerWithObjectAndMeta2 struct{}
   124  
   125  func (ControllerForHandlerWithObjectAndMeta2) Test3(ctx context.Context, req *TestForHandlerWithObjectAndMeta3Req) (res *TestForHandlerWithObjectAndMeta3Res, err error) {
   126  	return &TestForHandlerWithObjectAndMeta3Res{
   127  		Id:  1,
   128  		Age: req.Age,
   129  	}, nil
   130  }
   131  
   132  func (ControllerForHandlerWithObjectAndMeta2) Test4(ctx context.Context, req *TestForHandlerWithObjectAndMeta4Req) (res *TestForHandlerWithObjectAndMeta4Res, err error) {
   133  	return &TestForHandlerWithObjectAndMeta4Res{
   134  		Id:   1,
   135  		Name: req.Name,
   136  	}, nil
   137  }
   138  
   139  func Test_Router_Handler_Strict_WithObjectAndMeta(t *testing.T) {
   140  	s := g.Server(guid.S())
   141  	s.Use(ghttp.MiddlewareHandlerResponse)
   142  	s.Group("/", func(group *ghttp.RouterGroup) {
   143  		group.ALL("/", new(ControllerForHandlerWithObjectAndMeta1))
   144  	})
   145  	s.SetDumpRouterMap(false)
   146  	s.Start()
   147  	defer s.Shutdown()
   148  
   149  	time.Sleep(100 * time.Millisecond)
   150  	gtest.C(t, func(t *gtest.T) {
   151  		client := g.Client()
   152  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   153  
   154  		t.Assert(client.GetContent(ctx, "/"), `{"code":65,"message":"Not Found","data":null}`)
   155  		t.Assert(client.GetContent(ctx, "/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
   156  		t.Assert(client.GetContent(ctx, "/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
   157  		t.Assert(client.PostContent(ctx, "/custom-test2?age=18&name=john"), `{"code":65,"message":"Not Found","data":null}`)
   158  	})
   159  }
   160  
   161  func Test_Router_Handler_Strict_Group_Bind(t *testing.T) {
   162  	s := g.Server(guid.S())
   163  	s.Use(ghttp.MiddlewareHandlerResponse)
   164  	s.Group("/api/v1", func(group *ghttp.RouterGroup) {
   165  		group.Bind(
   166  			new(ControllerForHandlerWithObjectAndMeta1),
   167  			new(ControllerForHandlerWithObjectAndMeta2),
   168  		)
   169  	})
   170  	s.Group("/api/v2", func(group *ghttp.RouterGroup) {
   171  		group.Bind(
   172  			new(ControllerForHandlerWithObjectAndMeta1),
   173  			new(ControllerForHandlerWithObjectAndMeta2),
   174  		)
   175  	})
   176  	s.SetDumpRouterMap(false)
   177  	s.Start()
   178  	defer s.Shutdown()
   179  
   180  	time.Sleep(100 * time.Millisecond)
   181  	gtest.C(t, func(t *gtest.T) {
   182  		client := g.Client()
   183  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   184  
   185  		t.Assert(client.GetContent(ctx, "/"), `{"code":65,"message":"Not Found","data":null}`)
   186  		t.Assert(client.GetContent(ctx, "/api/v1/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
   187  		t.Assert(client.GetContent(ctx, "/api/v1/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
   188  		t.Assert(client.PostContent(ctx, "/api/v1/custom-test2?age=18&name=john"), `{"code":65,"message":"Not Found","data":null}`)
   189  
   190  		t.Assert(client.GetContent(ctx, "/api/v1/custom-test3?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
   191  		t.Assert(client.GetContent(ctx, "/api/v1/custom-test4?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
   192  
   193  		t.Assert(client.GetContent(ctx, "/api/v2/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
   194  		t.Assert(client.GetContent(ctx, "/api/v2/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
   195  		t.Assert(client.GetContent(ctx, "/api/v2/custom-test3?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
   196  		t.Assert(client.GetContent(ctx, "/api/v2/custom-test4?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
   197  	})
   198  }
   199  
   200  func Test_Issue1708(t *testing.T) {
   201  	type Test struct {
   202  		Name string `json:"name"`
   203  	}
   204  	type Req struct {
   205  		Page       int      `json:"page"       dc:"分页码"`
   206  		Size       int      `json:"size"       dc:"分页数量"`
   207  		TargetType string   `json:"targetType" v:"required#评论内容类型错误" dc:"评论类型: topic/ask/article/reply"`
   208  		TargetId   uint     `json:"targetId"   v:"required#评论目标ID错误" dc:"对应内容ID"`
   209  		Test       [][]Test `json:"test"`
   210  	}
   211  	type Res struct {
   212  		Page       int      `json:"page"       dc:"分页码"`
   213  		Size       int      `json:"size"       dc:"分页数量"`
   214  		TargetType string   `json:"targetType" v:"required#评论内容类型错误" dc:"评论类型: topic/ask/article/reply"`
   215  		TargetId   uint     `json:"targetId"   v:"required#评论目标ID错误" dc:"对应内容ID"`
   216  		Test       [][]Test `json:"test"`
   217  	}
   218  
   219  	s := g.Server(guid.S())
   220  	s.Use(ghttp.MiddlewareHandlerResponse)
   221  	s.BindHandler("/test", func(ctx context.Context, req *Req) (res *Res, err error) {
   222  		return &Res{
   223  			Page:       req.Page,
   224  			Size:       req.Size,
   225  			TargetType: req.TargetType,
   226  			TargetId:   req.TargetId,
   227  			Test:       req.Test,
   228  		}, nil
   229  	})
   230  	s.SetDumpRouterMap(false)
   231  	s.Start()
   232  	defer s.Shutdown()
   233  
   234  	time.Sleep(100 * time.Millisecond)
   235  	gtest.C(t, func(t *gtest.T) {
   236  		client := g.Client()
   237  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   238  		content := `
   239  {
   240      "targetType":"topic",
   241      "targetId":10785,
   242      "test":[
   243          [
   244              {
   245                  "name":"123"
   246              }
   247          ]
   248      ]
   249  }
   250  `
   251  		t.Assert(
   252  			client.PostContent(ctx, "/test", content),
   253  			`{"code":0,"message":"","data":{"page":0,"size":0,"targetType":"topic","targetId":10785,"test":[[{"name":"123"}]]}}`,
   254  		)
   255  	})
   256  }
   257  
   258  func Test_Custom_Slice_Type_Attribute(t *testing.T) {
   259  	type (
   260  		WhiteListKey    string
   261  		WhiteListValues []string
   262  		WhiteList       map[WhiteListKey]WhiteListValues
   263  	)
   264  	type Req struct {
   265  		Id   int
   266  		List WhiteList
   267  	}
   268  	type Res struct {
   269  		Content string
   270  	}
   271  
   272  	s := g.Server(guid.S())
   273  	s.Use(ghttp.MiddlewareHandlerResponse)
   274  	s.BindHandler("/test", func(ctx context.Context, req *Req) (res *Res, err error) {
   275  		return &Res{
   276  			Content: gjson.MustEncodeString(req),
   277  		}, nil
   278  	})
   279  	s.SetDumpRouterMap(false)
   280  	s.Start()
   281  	defer s.Shutdown()
   282  
   283  	time.Sleep(100 * time.Millisecond)
   284  	gtest.C(t, func(t *gtest.T) {
   285  		client := g.Client()
   286  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   287  		content := `
   288  {
   289      "id":1,
   290  	"list":{
   291  		"key": ["a", "b", "c"]
   292  	}
   293  }
   294  `
   295  		t.Assert(
   296  			client.PostContent(ctx, "/test", content),
   297  			`{"code":0,"message":"","data":{"Content":"{\"Id\":1,\"List\":{\"key\":[\"a\",\"b\",\"c\"]}}"}}`,
   298  		)
   299  	})
   300  }
   301  
   302  func Test_Router_Handler_Strict_WithGeneric(t *testing.T) {
   303  	type TestReq struct {
   304  		Age int
   305  	}
   306  	type TestGeneric[T any] struct {
   307  		Test T
   308  	}
   309  	type Test1Res struct {
   310  		Age TestGeneric[int]
   311  	}
   312  	type Test2Res TestGeneric[int]
   313  	type TestGenericRes[T any] struct {
   314  		Test T
   315  	}
   316  
   317  	s := g.Server(guid.S())
   318  	s.Use(ghttp.MiddlewareHandlerResponse)
   319  	s.BindHandler("/test1", func(ctx context.Context, req *TestReq) (res *Test1Res, err error) {
   320  		return &Test1Res{
   321  			Age: TestGeneric[int]{
   322  				Test: req.Age,
   323  			},
   324  		}, nil
   325  	})
   326  	s.BindHandler("/test1_slice", func(ctx context.Context, req *TestReq) (res []Test1Res, err error) {
   327  		return []Test1Res{
   328  			Test1Res{
   329  				Age: TestGeneric[int]{
   330  					Test: req.Age,
   331  				},
   332  			},
   333  		}, nil
   334  	})
   335  	s.BindHandler("/test2", func(ctx context.Context, req *TestReq) (res *Test2Res, err error) {
   336  		return &Test2Res{
   337  			Test: req.Age,
   338  		}, nil
   339  	})
   340  
   341  	s.BindHandler("/test2_slice", func(ctx context.Context, req *TestReq) (res []Test2Res, err error) {
   342  		return []Test2Res{
   343  			Test2Res{
   344  				Test: req.Age,
   345  			},
   346  		}, nil
   347  	})
   348  
   349  	s.BindHandler("/test3", func(ctx context.Context, req *TestReq) (res *TestGenericRes[int], err error) {
   350  		return &TestGenericRes[int]{
   351  			Test: req.Age,
   352  		}, nil
   353  	})
   354  
   355  	s.SetDumpRouterMap(false)
   356  	s.Start()
   357  	defer s.Shutdown()
   358  
   359  	s.BindHandler("/test3_slice", func(ctx context.Context, req *TestReq) (res []TestGenericRes[int], err error) {
   360  		return []TestGenericRes[int]{
   361  			TestGenericRes[int]{
   362  				Test: req.Age,
   363  			},
   364  		}, nil
   365  	})
   366  
   367  	time.Sleep(100 * time.Millisecond)
   368  	gtest.C(t, func(t *gtest.T) {
   369  		client := g.Client()
   370  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   371  
   372  		t.Assert(client.GetContent(ctx, "/test1?age=1"), `{"code":0,"message":"","data":{"Age":{"Test":1}}}`)
   373  		t.Assert(client.GetContent(ctx, "/test1_slice?age=1"), `{"code":0,"message":"","data":[{"Age":{"Test":1}}]}`)
   374  		t.Assert(client.GetContent(ctx, "/test2?age=2"), `{"code":0,"message":"","data":{"Test":2}}`)
   375  		t.Assert(client.GetContent(ctx, "/test2_slice?age=2"), `{"code":0,"message":"","data":[{"Test":2}]}`)
   376  		t.Assert(client.GetContent(ctx, "/test3?age=3"), `{"code":0,"message":"","data":{"Test":3}}`)
   377  		t.Assert(client.GetContent(ctx, "/test3_slice?age=3"), `{"code":0,"message":"","data":[{"Test":3}]}`)
   378  	})
   379  }
   380  
   381  type ParameterCaseSensitiveController struct{}
   382  
   383  type ParameterCaseSensitiveControllerPathReq struct {
   384  	g.Meta `path:"/api/*path" method:"post"`
   385  	Path   string
   386  }
   387  
   388  type ParameterCaseSensitiveControllerPathRes struct {
   389  	Path string
   390  }
   391  
   392  func (c *ParameterCaseSensitiveController) Path(
   393  	ctx context.Context,
   394  	req *ParameterCaseSensitiveControllerPathReq,
   395  ) (res *ParameterCaseSensitiveControllerPathRes, err error) {
   396  	return &ParameterCaseSensitiveControllerPathRes{Path: req.Path}, nil
   397  }
   398  
   399  func Test_Router_Handler_Strict_ParameterCaseSensitive(t *testing.T) {
   400  	s := g.Server(guid.S())
   401  	s.Use(ghttp.MiddlewareHandlerResponse)
   402  	s.Group("/", func(group *ghttp.RouterGroup) {
   403  		group.Bind(&ParameterCaseSensitiveController{})
   404  	})
   405  	s.SetDumpRouterMap(false)
   406  	s.Start()
   407  	defer s.Shutdown()
   408  
   409  	time.Sleep(100 * time.Millisecond)
   410  	gtest.C(t, func(t *gtest.T) {
   411  		client := g.Client()
   412  		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
   413  
   414  		for i := 0; i < 1000; i++ {
   415  			t.Assert(
   416  				client.PostContent(ctx, "/api/111", `{"Path":"222"}`),
   417  				`{"code":0,"message":"","data":{"Path":"222"}}`,
   418  			)
   419  		}
   420  	})
   421  }