mirror of
https://gitee.com/johng/gf
synced 2026-06-26 17:35:40 +08:00
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.
**Type alias migration to `any`:**
* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)
**Generated code and templates:**
* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
**Container and utility updates:**
* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)
These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
597 lines
16 KiB
Go
597 lines
16 KiB
Go
// 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]any
|
|
}
|
|
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.BindHandler("/parseErr", func(r *ghttp.Request) {
|
|
var user User
|
|
err := r.Parse(user)
|
|
r.Response.WriteExit(err != nil)
|
|
})
|
|
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`)
|
|
t.Assert(client.PostContent(ctx, "/parseErr", `{"id":1,"name":"john","map":{"id":1,"score":100}}`), true)
|
|
})
|
|
}
|
|
|
|
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`)
|
|
})
|
|
}
|
|
|
|
// https://github.com/gogf/gf/pull/4143
|
|
func Test_Params_ParseForm_FixMakeBodyRepeatableRead(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)
|
|
}
|
|
hasBody := len(r.GetBody()) > 0
|
|
r.Response.WriteExit(hasBody)
|
|
})
|
|
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"), `false`)
|
|
t.Assert(c.GetContent(ctx, "/parse-form", g.Map{
|
|
"id": 1,
|
|
"name": "john",
|
|
}), false)
|
|
t.Assert(c.PostContent(ctx, "/parse-form"), `false`)
|
|
t.Assert(c.PostContent(ctx, "/parse-form", g.Map{
|
|
"id": 1,
|
|
"name": "john",
|
|
}), true)
|
|
})
|
|
}
|
|
|
|
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`)
|
|
})
|
|
}
|