Merge branch 'master' of https://github.com/gogf/gf into feature_1.8.0

This commit is contained in:
John
2019-07-12 22:02:16 +08:00
10 changed files with 484 additions and 19 deletions

View File

@ -40,7 +40,19 @@ func TagMapField(pointer interface{}, priority []string, recursive bool) map[str
if v, ok := pointer.(reflect.Value); ok {
fields = structs.Fields(v.Interface())
} else {
fields = structs.Fields(pointer)
rv := reflect.ValueOf(pointer)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
// If pointer is type of **struct and nil, then automatically create a temporary struct,
// which is used for structs.Fields.
if kind == reflect.Ptr && (!rv.IsValid() || rv.IsNil()) {
fields = structs.Fields(reflect.New(rv.Type().Elem()).Elem().Interface())
} else {
fields = structs.Fields(pointer)
}
}
tag := ""
name := ""

View File

@ -7,29 +7,32 @@
package ghttp
import (
"strings"
"github.com/gogf/gf/g/encoding/gurl"
"github.com/gogf/gf/g/util/gconv"
"strings"
)
// 构建请求参数参数支持任意数据类型常见参数类型为string/map。
// 如果参数为map类型参数值将会进行urlencode编码。
func BuildParams(params interface{}) (encodedParamStr string) {
m := gconv.Map(params)
// 如果参数为map类型参数值将会进行urlencode编码;可以通过 noUrlEncode:true 参数取消编码
func BuildParams(params interface{}, noUrlEncode ...bool) (encodedParamStr string) {
m, urlEncode := gconv.Map(params), true
if len(m) == 0 {
return gconv.String(params)
}
if len(noUrlEncode) == 1 {
urlEncode = !noUrlEncode[0]
}
s := ""
for k, v := range m {
if len(encodedParamStr) > 0 {
encodedParamStr += "&"
}
s = gconv.String(v)
if len(s) > 6 && strings.Compare(s[0:6], "@file:") == 0 {
encodedParamStr += k + "=" + s
} else {
encodedParamStr += k + "=" + gurl.Encode(s)
if urlEncode && len(s) > 6 && strings.Compare(s[0:6], "@file:") != 0 {
s = gurl.Encode(s)
}
encodedParamStr += k + "=" + s
}
return
}

View File

@ -0,0 +1,67 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). 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/g/util/gvalid"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g/test/gtest"
)
func Test_Params_Struct(t *testing.T) {
type User struct {
Id int
Name string
Pass1 string `params:"password1"`
Pass2 string `params:"password2" gvalid:"passwd1 @required|length:2,20|password3||密码强度不足"`
}
p := ports.PopRand()
s := g.Server(p)
s.BindHandler("/struct1", func(r *ghttp.Request) {
if m := r.GetMap(); len(m) > 0 {
user := new(User)
r.GetToStruct(user)
r.Response.Write(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)
r.GetToStruct(&user)
r.Response.Write(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)
r.GetToStruct(user)
err := gvalid.CheckStruct(user, nil)
r.Response.Write(err.Maps())
}
})
s.SetPort(p)
s.SetDumpRouteMap(false)
s.Start()
defer s.Shutdown()
// 等待启动完成
time.Sleep(time.Second)
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
gtest.Assert(client.GetContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`)
gtest.Assert(client.PostContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`)
gtest.Assert(client.PostContent("/struct2", `id=1&name=john&password1=123&password2=456`), `1john123456`)
gtest.Assert(client.PostContent("/struct-valid", `id=1&name=john&password1=123&password2=0`), `{"passwd1":{"length":"字段长度为2到20个字符","password3":"密码格式不合法密码格式为任意6-18位的可见字符必须包含大小写字母、数字和特殊字符"}}`)
})
}

View File

@ -0,0 +1,92 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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.
// go test *.go -bench=".*" -benchmem
package gfcache_test
import (
"io/ioutil"
"os"
"testing"
"time"
"github.com/gogf/gf/g/os/gfcache"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
)
func TestGetContents(t *testing.T) {
gtest.Case(t, func() {
var f *os.File
var err error
fileName := "test"
strTest := "123"
if !gfile.Exists(fileName) {
f, err = ioutil.TempFile("", fileName)
if err != nil {
t.Error("create file fail")
}
}
defer f.Close()
defer os.Remove(f.Name())
if gfile.Exists(f.Name()) {
f, err = gfile.OpenFile(f.Name(), os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
t.Error("file open fail", err)
}
err = gfile.PutContents(f.Name(), strTest)
if err != nil {
t.Error("write error", err)
}
cache := gfcache.GetContents(f.Name(), 1)
gtest.Assert(cache, strTest)
}
})
gtest.Case(t, func() {
var f *os.File
var err error
fileName := "test2"
strTest := "123"
if !gfile.Exists(fileName) {
f, err = ioutil.TempFile("", fileName)
if err != nil {
t.Error("create file fail")
}
}
defer f.Close()
defer os.Remove(f.Name())
if gfile.Exists(f.Name()) {
cache := gfcache.GetContents(f.Name())
f, err = gfile.OpenFile(f.Name(), os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
t.Error("file open fail", err)
}
err = gfile.PutContents(f.Name(), strTest)
if err != nil {
t.Error("write error", err)
}
gtest.Assert(cache, "")
time.Sleep(100 * time.Millisecond)
}
})
}

View File

@ -138,11 +138,11 @@ func Check(value interface{}, rules string, msgs interface{}, params ...interfac
for i := 0; ; {
array := strings.Split(ruleItems[i], ":")
if _, ok := allSupportedRules[array[0]]; !ok {
if i > 0 {
if i > 0 && ruleItems[i-1][:5] == "regex" {
ruleItems[i-1] += "|" + ruleItems[i]
ruleItems = append(ruleItems[:i], ruleItems[i+1:]...)
} else {
return newErrorStr("invalid_rules", "invalid rules:"+rules)
return newErrorStr("parse_error", "invalid rules:"+rules)
}
} else {
i++

58
g/util/gvalid/gvalid_unit_basic_all_test.go Normal file → Executable file
View File

@ -7,12 +7,29 @@
package gvalid_test
import (
"testing"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gvalid"
"testing"
)
func Test_Check(t *testing.T) {
gtest.Case(t, func() {
rule := "abc:6,16"
val1 := 0
val2 := 7
val3 := 20
err1 := gvalid.Check(val1, rule, nil)
err2 := gvalid.Check(val2, rule, nil)
err3 := gvalid.Check(val3, rule, nil)
gtest.Assert(err1, "invalid rules:abc:6,16")
gtest.Assert(err2, "invalid rules:abc:6,16")
gtest.Assert(err3, "invalid rules:abc:6,16")
})
}
func Test_Required(t *testing.T) {
if m := gvalid.Check("1", "required", nil); m != nil {
t.Error(m)
@ -540,22 +557,44 @@ func Test_Length(t *testing.T) {
func Test_MinLength(t *testing.T) {
rule := "min-length:6"
msgs := map[string]string{
"min-length": "地址长度至少为:min位",
}
if m := gvalid.Check("123456", rule, nil); m != nil {
t.Error(m)
}
if m := gvalid.Check("12345", rule, nil); m == nil {
t.Error("长度校验失败")
}
if m := gvalid.Check("12345", rule, msgs); m == nil {
t.Error("长度校验失败")
}
rule2 := "min-length:abc"
if m := gvalid.Check("123456", rule2, nil); m == nil {
t.Error("长度校验失败")
}
}
func Test_MaxLength(t *testing.T) {
rule := "max-length:6"
msgs := map[string]string{
"max-length": "地址长度至大为:max位",
}
if m := gvalid.Check("12345", rule, nil); m != nil {
t.Error(m)
}
if m := gvalid.Check("1234567", rule, nil); m == nil {
t.Error("长度校验失败")
}
if m := gvalid.Check("1234567", rule, msgs); m == nil {
t.Error("长度校验失败")
}
rule2 := "max-length:abc"
if m := gvalid.Check("123456", rule2, nil); m == nil {
t.Error("长度校验失败")
}
}
func Test_Between(t *testing.T) {
@ -566,6 +605,9 @@ func Test_Between(t *testing.T) {
if m := gvalid.Check(10.02, rule, nil); m == nil {
t.Error("大小范围校验失败")
}
if m := gvalid.Check("a", rule, nil); m == nil {
t.Error("大小范围校验失败")
}
}
func Test_Min(t *testing.T) {
@ -575,14 +617,21 @@ func Test_Min(t *testing.T) {
val2 := "99"
val3 := "100"
val4 := "1000"
val5 := "a"
err1 := gvalid.Check(val1, rule, nil)
err2 := gvalid.Check(val2, rule, nil)
err3 := gvalid.Check(val3, rule, nil)
err4 := gvalid.Check(val4, rule, nil)
err5 := gvalid.Check(val5, rule, nil)
gtest.AssertNE(err1, nil)
gtest.AssertNE(err2, nil)
gtest.Assert(err3, nil)
gtest.Assert(err4, nil)
gtest.AssertNE(err5, nil)
rule2 := "min:a"
err6 := gvalid.Check(val1, rule2, nil)
gtest.AssertNE(err6, nil)
})
}
@ -593,14 +642,21 @@ func Test_Max(t *testing.T) {
val2 := "99"
val3 := "100"
val4 := "1000"
val5 := "a"
err1 := gvalid.Check(val1, rule, nil)
err2 := gvalid.Check(val2, rule, nil)
err3 := gvalid.Check(val3, rule, nil)
err4 := gvalid.Check(val4, rule, nil)
err5 := gvalid.Check(val5, rule, nil)
gtest.Assert(err1, nil)
gtest.Assert(err2, nil)
gtest.Assert(err3, nil)
gtest.AssertNE(err4, nil)
gtest.AssertNE(err5, nil)
rule2 := "max:a"
err6 := gvalid.Check(val1, rule2, nil)
gtest.AssertNE(err6, nil)
})
}

85
g/util/gvalid/gvalid_unit_checkmap_test.go Normal file → Executable file
View File

@ -7,12 +7,19 @@
package gvalid_test
import (
"testing"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gvalid"
"testing"
)
func Test_CheckMap(t *testing.T) {
var params interface{}
if m := gvalid.CheckMap(params, nil, nil); m == nil {
t.Error("CheckMap校验失败")
}
kvmap := map[string]interface{}{
"id": "0",
"name": "john",
@ -50,6 +57,82 @@ func Test_CheckMap(t *testing.T) {
if m := gvalid.CheckMap(kvmap, rules, msgs); m != nil {
t.Error(m)
}
kvmap = map[string]interface{}{
"id": "1",
"name": "john",
}
rules = map[string]string{
"id": "",
"name": "",
}
msgs = map[string]interface{}{
"id": "ID不能为空|ID范围应当为:min到:max",
"name": map[string]string{
"required": "名称不能为空",
"length": "名称长度为:min到:max个字符",
},
}
if m := gvalid.CheckMap(kvmap, rules, msgs); m != nil {
t.Error(m)
}
kvmap = map[string]interface{}{
"id": "1",
"name": "john",
}
rules2 := []string{
"@required|between:1,100",
"@required|length:4,16",
}
msgs = map[string]interface{}{
"id": "ID不能为空|ID范围应当为:min到:max",
"name": map[string]string{
"required": "名称不能为空",
"length": "名称长度为:min到:max个字符",
},
}
if m := gvalid.CheckMap(kvmap, rules2, msgs); m != nil {
t.Error(m)
}
kvmap = map[string]interface{}{
"id": "1",
"name": "john",
}
rules2 = []string{
"id@required|between:1,100",
"name@required|length:4,16#名称不能为空|",
}
msgs = map[string]interface{}{
"id": "ID不能为空|ID范围应当为:min到:max",
"name": map[string]string{
"required": "名称不能为空",
"length": "名称长度为:min到:max个字符",
},
}
if m := gvalid.CheckMap(kvmap, rules2, msgs); m != nil {
t.Error(m)
}
kvmap = map[string]interface{}{
"id": "1",
"name": "john",
}
rules2 = []string{
"id@required|between:1,100",
"name@required|length:4,16#名称不能为空",
}
msgs = map[string]interface{}{
"id": "ID不能为空|ID范围应当为:min到:max",
"name": map[string]string{
"required": "名称不能为空",
"length": "名称长度为:min到:max个字符",
},
}
if m := gvalid.CheckMap(kvmap, rules2, msgs); m != nil {
t.Error(m)
}
}
// 如果值为nil并且不需要require*验证时,其他验证失效

132
g/util/gvalid/gvalid_unit_checkstruct_test.go Normal file → Executable file
View File

@ -16,6 +16,77 @@ import (
)
func Test_CheckStruct(t *testing.T) {
gtest.Case(t, func() {
type Object struct {
Name string
Age int
}
rules := []string{
"@required|length:6,16",
"@between:18,30",
}
msgs := map[string]interface{}{
"Name": map[string]string{
"required": "名称不能为空",
"length": "名称长度为:min到:max个字符",
},
"Age": "年龄为18到30周岁",
}
obj := &Object{"john", 16}
err := gvalid.CheckStruct(obj, rules, msgs)
gtest.Assert(err, nil)
})
gtest.Case(t, func() {
type Object struct {
Name string
Age int
}
rules := []string{
"Name@required|length:6,16#名称不能为空",
"Age@between:18,30",
}
msgs := map[string]interface{}{
"Name": map[string]string{
"required": "名称不能为空",
"length": "名称长度为:min到:max个字符",
},
"Age": "年龄为18到30周岁",
}
obj := &Object{"john", 16}
err := gvalid.CheckStruct(obj, rules, msgs)
gtest.AssertNE(err, nil)
gtest.Assert(len(err.Maps()), 2)
gtest.Assert(err.Maps()["Name"]["required"], "")
gtest.Assert(err.Maps()["Name"]["length"], "名称长度为6到16个字符")
gtest.Assert(err.Maps()["Age"]["between"], "年龄为18到30周岁")
})
gtest.Case(t, func() {
type Object struct {
Name string
Age int
}
rules := []string{
"Name@required|length:6,16#名称不能为空|",
"Age@between:18,30",
}
msgs := map[string]interface{}{
"Name": map[string]string{
"required": "名称不能为空",
"length": "名称长度为:min到:max个字符",
},
"Age": "年龄为18到30周岁",
}
obj := &Object{"john", 16}
err := gvalid.CheckStruct(obj, rules, msgs)
gtest.AssertNE(err, nil)
gtest.Assert(len(err.Maps()), 2)
gtest.Assert(err.Maps()["Name"]["required"], "")
gtest.Assert(err.Maps()["Name"]["length"], "名称长度为6到16个字符")
gtest.Assert(err.Maps()["Age"]["between"], "年龄为18到30周岁")
})
gtest.Case(t, func() {
type Object struct {
Name string
@ -54,6 +125,27 @@ func Test_CheckStruct(t *testing.T) {
gtest.Assert(err.Maps()["password"]["required"], "登录密码不能为空")
})
gtest.Case(t, func() {
type LoginRequest struct {
Username string `json:"username" gvalid:"@required#用户名不能为空"`
Password string `json:"password" gvalid:"@required#登录密码不能为空"`
}
var login LoginRequest
err := gvalid.CheckStruct(login, nil)
gtest.Assert(err, nil)
})
gtest.Case(t, func() {
type LoginRequest struct {
username string `json:"username" gvalid:"username@required#用户名不能为空"`
Password string `json:"password" gvalid:"password@required#登录密码不能为空"`
}
var login LoginRequest
err := gvalid.CheckStruct(login, nil)
gtest.AssertNE(err, nil)
gtest.Assert(err.Maps()["password"]["required"], "登录密码不能为空")
})
// gvalid tag
gtest.Case(t, func() {
type User struct {
@ -73,6 +165,46 @@ func Test_CheckStruct(t *testing.T) {
gtest.Assert(err.Maps()["uid"]["min"], "ID不能为空")
})
gtest.Case(t, func() {
type User struct {
Id int `gvalid:"uid@required|min:10#|ID不能为空"`
Age int `gvalid:"age@required#年龄不能为空"`
Username string `json:"username" gvalid:"username@required#用户名不能为空"`
Password string `json:"password" gvalid:"password@required#登录密码不能为空"`
}
user := &User{
Id: 1,
Username: "john",
Password: "123456",
}
rules := []string{
"username@required#用户名不能为空",
}
err := gvalid.CheckStruct(user, rules)
gtest.AssertNE(err, nil)
gtest.Assert(len(err.Maps()), 1)
gtest.Assert(err.Maps()["uid"]["min"], "ID不能为空")
})
gtest.Case(t, func() {
type User struct {
Id int `gvalid:"uid@required|min:10#ID不能为空"`
Age int `gvalid:"age@required#年龄不能为空"`
Username string `json:"username" gvalid:"username@required#用户名不能为空"`
Password string `json:"password" gvalid:"password@required#登录密码不能为空"`
}
user := &User{
Id: 1,
Username: "john",
Password: "123456",
}
err := gvalid.CheckStruct(user, nil)
gtest.AssertNE(err, nil)
gtest.Assert(len(err.Maps()), 1)
})
// valid tag
gtest.Case(t, func() {
type User struct {

24
g/util/gvalid/gvalid_unit_customerror_test.go Normal file → Executable file
View File

@ -7,11 +7,33 @@
package gvalid_test
import (
"github.com/gogf/gf/g/util/gvalid"
"strings"
"testing"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gvalid"
)
func Test_Map(t *testing.T) {
rule := "ipv4"
val := "0.0.0"
msg := map[string]string{
"ipv4": "IPv4地址格式不正确",
}
err := gvalid.Check(val, rule, nil)
gtest.Assert(err.Map(), msg)
}
func Test_FirstString(t *testing.T) {
rule := "ipv4"
val := "0.0.0"
err := gvalid.Check(val, rule, nil)
n := err.FirstString()
gtest.Assert(n, "IPv4地址格式不正确")
}
func Test_SetDefaultErrorMsgs(t *testing.T) {
rule := "integer|length:6,16"
msgs := map[string]string{

View File

@ -8,14 +8,12 @@ import (
// same校验
func main() {
type User struct {
Password string `gvalid:"password@password"`
ConfirmPassword string `gvalid:"confirm_password@password|same:password#|密码与确认密码不一致"`
Pass string `gvalid:"passwd1 @required|length:2,20|password3||密码强度不足"`
}
user := &User{
Password: "123456",
ConfirmPassword: "",
Pass: "1",
}
g.Dump(gvalid.CheckStruct(user, nil))
g.Dump(gvalid.CheckStruct(user, nil).Maps())
}