diff --git a/.example/net/ghttp/server/request/request_struct.go b/.example/net/ghttp/server/request/request_struct.go index 65ebcdf2e..c2737f6ec 100644 --- a/.example/net/ghttp/server/request/request_struct.go +++ b/.example/net/ghttp/server/request/request_struct.go @@ -5,20 +5,20 @@ import ( "github.com/gogf/gf/net/ghttp" ) -type User struct { - Uid int `json:"uid"` - Name string `json:"name" params:"username"` - Pass1 string `json:"pass1" params:"password1,userpass1"` - Pass2 string `json:"pass2" params:"password3,userpass2"` -} - func main() { + type User struct { + Uid int `json:"uid"` + Name string `json:"name" p:"username"` + Pass1 string `json:"pass1" p:"password1"` + Pass2 string `json:"pass2" p:"password2"` + } + s := g.Server() s.BindHandler("/user", func(r *ghttp.Request) { - user := new(User) - r.GetToStruct(user) - //r.GetPostToStruct(user) - //r.GetQueryToStruct(user) + var user *User + if err := r.Parse(&user); err != nil { + panic(err) + } r.Response.WriteJson(user) }) s.SetPort(8199) diff --git a/.example/other/test.go b/.example/other/test.go index b5841bdbe..249e5bb78 100644 --- a/.example/other/test.go +++ b/.example/other/test.go @@ -1,13 +1,17 @@ package main import ( + "fmt" "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" ) func main() { - db := g.DB() - // 开启调试模式,以便于记录所有执行的SQL - db.SetDebug(true) - - db.Table("user").Delete("score < ", 60) + s := g.Server() + s.BindHandler("/test", func(r *ghttp.Request) { + fmt.Println(r.GetBody()) + r.Response.Write(r.GetBody()) + }) + s.SetPort(8199) + s.Run() } diff --git a/.example/other/test2.go b/.example/other/test2.go new file mode 100644 index 000000000..0c08a31b0 --- /dev/null +++ b/.example/other/test2.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/net/ghttp" +) + +func main() { + r := ghttp.PostContent("http://127.0.0.1:8199/test", `1john123Abc!@#123Abc!@#`) + fmt.Println(r) +} diff --git a/encoding/gxml/gxml.go b/encoding/gxml/gxml.go index 5155d29c4..df096f31b 100644 --- a/encoding/gxml/gxml.go +++ b/encoding/gxml/gxml.go @@ -8,7 +8,6 @@ package gxml import ( - "fmt" "strings" "github.com/clbanning/mxj" @@ -16,7 +15,7 @@ import ( "github.com/gogf/gf/text/gregex" ) -// 将XML内容解析为map变量 +// Decode parses into and returns as map. func Decode(content []byte) (map[string]interface{}, error) { res, err := convert(content) if err != nil { @@ -25,23 +24,42 @@ func Decode(content []byte) (map[string]interface{}, error) { return mxj.NewMapXml(res) } -// 将map变量解析为XML格式内容 -func Encode(v map[string]interface{}, rootTag ...string) ([]byte, error) { - return mxj.Map(v).Xml(rootTag...) +// DecodeWithoutRoot parses into a map, and returns the map without root level. +func DecodeWithoutRoot(content []byte) (map[string]interface{}, error) { + res, err := convert(content) + if err != nil { + return nil, err + } + m, err := mxj.NewMapXml(res) + if err != nil { + return nil, err + } + for _, v := range m { + if r, ok := v.(map[string]interface{}); ok { + return r, nil + } + } + return m, nil } -func EncodeWithIndent(v map[string]interface{}, rootTag ...string) ([]byte, error) { - return mxj.Map(v).XmlIndent("", "\t", rootTag...) +// Encode encodes map to a XML format content as bytes. +// The optional parameter is used to specify the XML root tag. +func Encode(m map[string]interface{}, rootTag ...string) ([]byte, error) { + return mxj.Map(m).Xml(rootTag...) } -// XML格式内容直接转换为JSON格式内容 +// Encode encodes map to a XML format content as bytes with indent. +// The optional parameter is used to specify the XML root tag. +func EncodeWithIndent(m map[string]interface{}, rootTag ...string) ([]byte, error) { + return mxj.Map(m).XmlIndent("", "\t", rootTag...) +} + +// ToJson converts as XML format into JSON format bytes. func ToJson(content []byte) ([]byte, error) { res, err := convert(content) if err != nil { - fmt.Println("convert error. ", err) return nil, err } - mv, err := mxj.NewMapXml(res) if err == nil { return mv.Json() @@ -50,7 +68,7 @@ func ToJson(content []byte) ([]byte, error) { } } -// XML字符集预处理 +// convert converts the encoding of given XML content from XML root tag into UTF-8 encoding content. func convert(xml []byte) (res []byte, err error) { patten := `<\?xml.*encoding\s*=\s*['|"](.*?)['|"].*\?>` matchStr, err := gregex.MatchString(patten, string(xml)) diff --git a/net/ghttp/ghttp_client_request.go b/net/ghttp/ghttp_client_request.go index 06acb3631..a79f1a0f2 100644 --- a/net/ghttp/ghttp_client_request.go +++ b/net/ghttp/ghttp_client_request.go @@ -11,6 +11,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/gogf/gf/text/gregex" "io" "mime/multipart" "net/http" @@ -91,10 +92,11 @@ func (c *Client) Post(url string, data ...interface{}) (resp *ClientResponse, er // Custom Content-Type. req.Header.Set("Content-Type", v) } else { - // Auto detecting and setting the post content format: JSON. if json.Valid(paramBytes) { + // Auto detecting and setting the post content format: JSON. req.Header.Set("Content-Type", "application/json") - } else { + } else if gregex.IsMatchString(`^\w+=.+`, param) { + // If the parameters passed like "name=value", it then uses form type. req.Header.Set("Content-Type", "application/x-www-form-urlencoded") } } diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index 3939d60e3..4acf684c9 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -7,15 +7,39 @@ package ghttp import ( + "bytes" + "encoding/json" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/encoding/gjson" "github.com/gogf/gf/encoding/gurl" + "github.com/gogf/gf/encoding/gxml" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/gvalid" "io/ioutil" "mime/multipart" ) +var ( + // xmlHeaderBytes is the most common XML format header. + xmlHeaderBytes = []byte(" 0 { - r.bodyMap, _ = gstr.Parse(body) + if body := r.GetBody(); len(body) > 0 { + // Trim space/new line characters. + body = bytes.TrimSpace(body) + // JSON format checks. + if body[0] == '{' && body[len(body)-1] == '}' { + _ = json.Unmarshal(body, &r.bodyMap) + } + // XML format checks. + if len(body) > 5 && bytes.EqualFold(body[:5], xmlHeaderBytes) { + r.bodyMap, _ = gxml.DecodeWithoutRoot(body) + } + if body[0] == '<' && body[len(body)-1] == '>' { + r.bodyMap, _ = gxml.DecodeWithoutRoot(body) + } + // Default parameters decoding. + if r.bodyMap == nil { + r.bodyMap, _ = gstr.Parse(r.GetBodyString()) + } } } -// ParseForm parses the request form for HTTP method PUT, POST, PATCH. +// parseForm parses the request form for HTTP method PUT, POST, PATCH. // The form data is pared into r.formMap. -func (r *Request) ParseForm() { +func (r *Request) parseForm() { if r.parsedForm { return } @@ -217,7 +258,7 @@ func (r *Request) ParseForm() { panic(err) } } else { - r.ParseBody() + r.parseBody() if len(r.bodyMap) > 0 { r.formMap = r.bodyMap } @@ -227,7 +268,7 @@ func (r *Request) ParseForm() { // GetMultipartForm parses and returns the form as multipart form. func (r *Request) GetMultipartForm() *multipart.Form { - r.ParseForm() + r.parseForm() return r.MultipartForm } diff --git a/net/ghttp/ghttp_request_param_form.go b/net/ghttp/ghttp_request_param_form.go index 5ceeac585..7f9eefc7c 100644 --- a/net/ghttp/ghttp_request_param_form.go +++ b/net/ghttp/ghttp_request_param_form.go @@ -14,7 +14,7 @@ import ( // SetForm sets custom form value with key-value pair. func (r *Request) SetForm(key string, value interface{}) { - r.ParseForm() + r.parseForm() if r.formMap == nil { r.formMap = make(map[string]interface{}) } @@ -25,7 +25,7 @@ func (r *Request) SetForm(key string, value interface{}) { // It returns if does not exist in the form. // It returns nil if is not passed. func (r *Request) GetForm(key string, def ...interface{}) interface{} { - r.ParseForm() + r.parseForm() if len(r.formMap) > 0 { if v, ok := r.formMap[key]; ok { return v @@ -105,7 +105,7 @@ func (r *Request) GetFormInterfaces(key string, def ...interface{}) []interface{ // The parameter specifies the keys retrieving from client parameters, // the associated values are the default values if the client does not pass. func (r *Request) GetFormMap(kvMap ...map[string]interface{}) map[string]interface{} { - r.ParseForm() + r.parseForm() if len(kvMap) > 0 && kvMap[0] != nil { if len(r.formMap) == 0 { return kvMap[0] @@ -158,7 +158,7 @@ func (r *Request) GetFormMapStrVar(kvMap ...map[string]interface{}) map[string]* // given struct object. Note that the parameter is a pointer to the struct object. // The optional parameter is used to specify the key to attribute mapping. func (r *Request) GetFormStruct(pointer interface{}, mapping ...map[string]string) error { - r.ParseForm() + r.parseForm() tagMap := structs.TagMapName(pointer, paramTagPriority, true) if len(mapping) > 0 { for k, v := range mapping[0] { diff --git a/net/ghttp/ghttp_request_param_post.go b/net/ghttp/ghttp_request_param_post.go index e03a27f89..913b95454 100644 --- a/net/ghttp/ghttp_request_param_post.go +++ b/net/ghttp/ghttp_request_param_post.go @@ -18,14 +18,16 @@ import ( // // Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote // in order of priority: form > body. +// +// Deprecated. func (r *Request) GetPost(key string, def ...interface{}) interface{} { - r.ParseForm() + r.parseForm() if len(r.formMap) > 0 { if v, ok := r.formMap[key]; ok { return v } } - r.ParseBody() + r.parseBody() if len(r.bodyMap) > 0 { if v, ok := r.bodyMap[key]; ok { return v @@ -37,66 +39,82 @@ func (r *Request) GetPost(key string, def ...interface{}) interface{} { return nil } +// Deprecated. func (r *Request) GetPostVar(key string, def ...interface{}) *gvar.Var { return gvar.New(r.GetPost(key, def...)) } +// Deprecated. func (r *Request) GetPostString(key string, def ...interface{}) string { return r.GetPostVar(key, def...).String() } +// Deprecated. func (r *Request) GetPostBool(key string, def ...interface{}) bool { return r.GetPostVar(key, def...).Bool() } +// Deprecated. func (r *Request) GetPostInt(key string, def ...interface{}) int { return r.GetPostVar(key, def...).Int() } +// Deprecated. func (r *Request) GetPostInt32(key string, def ...interface{}) int32 { return r.GetPostVar(key, def...).Int32() } +// Deprecated. func (r *Request) GetPostInt64(key string, def ...interface{}) int64 { return r.GetPostVar(key, def...).Int64() } +// Deprecated. func (r *Request) GetPostInts(key string, def ...interface{}) []int { return r.GetPostVar(key, def...).Ints() } +// Deprecated. func (r *Request) GetPostUint(key string, def ...interface{}) uint { return r.GetPostVar(key, def...).Uint() } +// Deprecated. func (r *Request) GetPostUint32(key string, def ...interface{}) uint32 { return r.GetPostVar(key, def...).Uint32() } +// Deprecated. func (r *Request) GetPostUint64(key string, def ...interface{}) uint64 { return r.GetPostVar(key, def...).Uint64() } +// Deprecated. func (r *Request) GetPostFloat32(key string, def ...interface{}) float32 { return r.GetPostVar(key, def...).Float32() } +// Deprecated. func (r *Request) GetPostFloat64(key string, def ...interface{}) float64 { return r.GetPostVar(key, def...).Float64() } +// Deprecated. func (r *Request) GetPostFloats(key string, def ...interface{}) []float64 { return r.GetPostVar(key, def...).Floats() } +// Deprecated. func (r *Request) GetPostArray(key string, def ...interface{}) []string { return r.GetPostVar(key, def...).Strings() } +// Deprecated. func (r *Request) GetPostStrings(key string, def ...interface{}) []string { return r.GetPostVar(key, def...).Strings() } +// Deprecated. func (r *Request) GetPostInterfaces(key string, def ...interface{}) []interface{} { return r.GetPostVar(key, def...).Interfaces() } @@ -107,9 +125,11 @@ func (r *Request) GetPostInterfaces(key string, def ...interface{}) []interface{ // // Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote // in order of priority: form > body. +// +// Deprecated. func (r *Request) GetPostMap(kvMap ...map[string]interface{}) map[string]interface{} { - r.ParseForm() - r.ParseBody() + r.parseForm() + r.parseBody() var ok, filter bool if len(kvMap) > 0 && kvMap[0] != nil { filter = true @@ -146,6 +166,8 @@ func (r *Request) GetPostMap(kvMap ...map[string]interface{}) map[string]interfa // as map[string]string. The parameter specifies the keys // retrieving from client parameters, the associated values are the default values if the client // does not pass. +// +// Deprecated. func (r *Request) GetPostMapStrStr(kvMap ...map[string]interface{}) map[string]string { postMap := r.GetPostMap(kvMap...) if len(postMap) > 0 { @@ -162,6 +184,8 @@ func (r *Request) GetPostMapStrStr(kvMap ...map[string]interface{}) map[string]s // as map[string]*gvar.Var. The parameter specifies the keys // retrieving from client parameters, the associated values are the default values if the client // does not pass. +// +// Deprecated. func (r *Request) GetPostMapStrVar(kvMap ...map[string]interface{}) map[string]*gvar.Var { postMap := r.GetPostMap(kvMap...) if len(postMap) > 0 { @@ -178,6 +202,8 @@ func (r *Request) GetPostMapStrVar(kvMap ...map[string]interface{}) map[string]* // and converts them to given struct object. Note that the parameter is a pointer // to the struct object. The optional parameter is used to specify the key to // attribute mapping. +// +// Deprecated. func (r *Request) GetPostStruct(pointer interface{}, mapping ...map[string]string) error { tagMap := structs.TagMapName(pointer, paramTagPriority, true) if len(mapping) > 0 { @@ -189,6 +215,7 @@ func (r *Request) GetPostStruct(pointer interface{}, mapping ...map[string]strin } // GetPostToStruct is alias of GetQueryStruct. See GetPostStruct. +// // Deprecated. func (r *Request) GetPostToStruct(pointer interface{}, mapping ...map[string]string) error { return r.GetPostStruct(pointer, mapping...) diff --git a/net/ghttp/ghttp_request_param_query.go b/net/ghttp/ghttp_request_param_query.go index ba644a3e0..26d80511f 100644 --- a/net/ghttp/ghttp_request_param_query.go +++ b/net/ghttp/ghttp_request_param_query.go @@ -15,7 +15,7 @@ import ( // SetQuery sets custom query value with key-value pair. func (r *Request) SetQuery(key string, value interface{}) { - r.ParseQuery() + r.parseQuery() if r.queryMap == nil { r.queryMap = make(map[string]interface{}) } @@ -29,13 +29,13 @@ func (r *Request) SetQuery(key string, value interface{}) { // Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote // in order of priority: query > body. func (r *Request) GetQuery(key string, def ...interface{}) interface{} { - r.ParseQuery() + r.parseQuery() if len(r.queryMap) > 0 { if v, ok := r.queryMap[key]; ok { return v } } - r.ParseBody() + r.parseBody() if len(r.bodyMap) > 0 { if v, ok := r.bodyMap[key]; ok { return v @@ -118,8 +118,8 @@ func (r *Request) GetQueryInterfaces(key string, def ...interface{}) []interface // Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote // in order of priority: query > body. func (r *Request) GetQueryMap(kvMap ...map[string]interface{}) map[string]interface{} { - r.ParseQuery() - r.ParseBody() + r.parseQuery() + r.parseBody() var m map[string]interface{} if len(kvMap) > 0 && kvMap[0] != nil { if len(r.queryMap) == 0 && len(r.bodyMap) == 0 { @@ -193,7 +193,7 @@ func (r *Request) GetQueryMapStrVar(kvMap ...map[string]interface{}) map[string] // to the struct object. The optional parameter is used to specify the key to // attribute mapping. func (r *Request) GetQueryStruct(pointer interface{}, mapping ...map[string]string) error { - r.ParseQuery() + r.parseQuery() tagMap := structs.TagMapName(pointer, paramTagPriority, true) if len(mapping) > 0 { for k, v := range mapping[0] { diff --git a/net/ghttp/ghttp_request_param_request.go b/net/ghttp/ghttp_request_param_request.go index 5647dcfb6..ac11ef034 100644 --- a/net/ghttp/ghttp_request_param_request.go +++ b/net/ghttp/ghttp_request_param_request.go @@ -26,7 +26,7 @@ func (r *Request) GetRequest(key string, def ...interface{}) interface{} { value = r.GetForm(key) } if value == nil { - r.ParseBody() + r.parseBody() if len(r.bodyMap) > 0 { value = r.bodyMap[key] } @@ -168,9 +168,9 @@ func (r *Request) GetRequestInterfaces(key string, def ...interface{}) []interfa // Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote // in order of priority: router < query < body < form < custom. func (r *Request) GetRequestMap(kvMap ...map[string]interface{}) map[string]interface{} { - r.ParseQuery() - r.ParseForm() - r.ParseBody() + r.parseQuery() + r.parseForm() + r.parseBody() var ok, filter bool var length int if len(kvMap) > 0 && kvMap[0] != nil { diff --git a/net/ghttp/ghttp_unit_param_json_test.go b/net/ghttp/ghttp_unit_param_json_test.go index fe3392555..af024d931 100644 --- a/net/ghttp/ghttp_unit_param_json_test.go +++ b/net/ghttp/ghttp_unit_param_json_test.go @@ -17,7 +17,51 @@ import ( "github.com/gogf/gf/test/gtest" ) -func Test_Params_Json(t *testing.T) { +func Test_Params_Json_Request(t *testing.T) { + type User struct { + Id int + Name string + Time *time.Time + Pass1 string `p:"password1" v:"password1"` + Pass2 string `p:"password2" v:"required|length:2,20|password3|same:password1#||密码强度不足|两次密码不一致"` + } + p := ports.PopRand() + s := g.Server(p) + s.BindHandler("/get", func(r *ghttp.Request) { + r.Response.WriteExit(r.Get("id"), r.Get("name")) + }) + s.BindHandler("/map", func(r *ghttp.Request) { + if m := r.GetMap(); len(m) > 0 { + r.Response.WriteExit(m["id"], m["name"], m["password1"], m["password2"]) + } + }) + 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.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.Case(t, func() { + client := ghttp.NewClient() + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + + gtest.Assert(client.GetContent("/get", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john`) + gtest.Assert(client.GetContent("/map", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john123Abc!@#123Abc!@#`) + gtest.Assert(client.PostContent("/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john123Abc!@#123Abc!@#`) + gtest.Assert(client.PostContent("/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123"}`), `密码强度不足; 两次密码不一致`) + }) +} + +func Test_Params_Json_Response(t *testing.T) { type User struct { Uid int Name string diff --git a/net/ghttp/ghttp_unit_param_struct_test.go b/net/ghttp/ghttp_unit_param_struct_test.go index 341b0935e..cf39ebbbf 100644 --- a/net/ghttp/ghttp_unit_param_struct_test.go +++ b/net/ghttp/ghttp_unit_param_struct_test.go @@ -23,33 +23,49 @@ func Test_Params_Struct(t *testing.T) { Id int Name string Time *time.Time - Pass1 string `params:"password1"` - Pass2 string `params:"password2" gvalid:"passwd1 @required|length:2,20|password3#||密码强度不足"` + Pass1 string `p:"password1"` + Pass2 string `p:"password2" v:"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) + 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) - r.GetToStruct(&user) + if err := r.GetStruct(&user); err != nil { + r.Response.WriteExit(err) + } if user != nil { - r.Response.Write(user.Id, user.Name, user.Pass1, user.Pass2) + 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) - r.GetToStruct(user) - err := gvalid.CheckStruct(user, nil) - r.Response.Write(err.Maps()) + if err := r.GetStruct(user); err != nil { + r.Response.WriteExit(err) + } + if err := gvalid.CheckStruct(user, nil); 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.SetPort(p) @@ -65,6 +81,10 @@ func Test_Params_Struct(t *testing.T) { 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("/struct2", ``), ``) - gtest.Assert(client.PostContent("/struct-valid", `id=1&name=john&password1=123&password2=0`), `{"passwd1":{"length":"字段长度为2到20个字符","password3":"密码强度不足"}}`) + gtest.Assert(client.PostContent("/struct-valid", `id=1&name=john&password1=123&password2=0`), `字段长度为2到20个字符; 密码强度不足`) + gtest.Assert(client.PostContent("/parse", `id=1&name=john&password1=123&password2=0`), `字段长度为2到20个字符; 密码强度不足`) + gtest.Assert(client.GetContent("/parse", `id=1&name=john&password1=123&password2=456`), `密码强度不足`) + gtest.Assert(client.GetContent("/parse", `id=1&name=john&password1=123Abc!@#&password2=123Abc!@#`), `1john123Abc!@#123Abc!@#`) + gtest.Assert(client.PostContent("/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john123Abc!@#123Abc!@#`) }) } diff --git a/net/ghttp/ghttp_unit_param_xml_test.go b/net/ghttp/ghttp_unit_param_xml_test.go new file mode 100644 index 000000000..2c004614c --- /dev/null +++ b/net/ghttp/ghttp_unit_param_xml_test.go @@ -0,0 +1,65 @@ +// 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/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/test/gtest" +) + +func Test_Params_Xml_Request(t *testing.T) { + type User struct { + Id int + Name string + Time *time.Time + Pass1 string `p:"password1" v:"password1"` + Pass2 string `p:"password2" v:"required|length:2,20|password3|same:password1#||密码强度不足|两次密码不一致"` + } + p := ports.PopRand() + s := g.Server(p) + s.BindHandler("/get", func(r *ghttp.Request) { + r.Response.WriteExit(r.Get("id"), r.Get("name")) + }) + s.BindHandler("/map", func(r *ghttp.Request) { + if m := r.GetMap(); len(m) > 0 { + r.Response.WriteExit(m["id"], m["name"], m["password1"], m["password2"]) + } + }) + 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.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.Case(t, func() { + client := ghttp.NewClient() + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + + content1 := `1john123Abc!@#123Abc!@#` + content2 := `1john123Abc!@#123` + gtest.Assert(client.GetContent("/get", content1), `1john`) + gtest.Assert(client.PostContent("/get", content1), `1john`) + gtest.Assert(client.GetContent("/map", content1), `1john123Abc!@#123Abc!@#`) + gtest.Assert(client.PostContent("/map", content1), `1john123Abc!@#123Abc!@#`) + gtest.Assert(client.PostContent("/parse", content1), `1john123Abc!@#123Abc!@#`) + gtest.Assert(client.PostContent("/parse", content2), `密码强度不足; 两次密码不一致`) + }) +} diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index 0860aaa3a..5f5aaa850 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -87,7 +87,15 @@ var ( ) // SetTimeZone sets the time zone for current whole process. -// The parameter is an area string specifying corresponding time zone, eg: Asia/Shanghai. +// The parameter is an area string specifying corresponding time zone, +// eg: Asia/Shanghai. +// +// Note that the time zone database needed by LoadLocation may not be +// present on all systems, especially non-Unix systems. +// LoadLocation looks in the directory or uncompressed zip file +// named by the ZONEINFO environment variable, if any, then looks in +// known installation locations on Unix systems, +// and finally looks in $GOROOT/lib/time/zoneinfo.zip. func SetTimeZone(zone string) error { location, err := time.LoadLocation(zone) if err == nil { diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 0bc7ee8da..a117d3169 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -65,6 +65,13 @@ type CustomMsg = map[string]interface{} // 解析单条sequence tag,格式: [数值键名/别名@]校验规则[#错误提示], // 其中校验规则如果有多个那么以"|"符号分隔,错误提示同理。 func parseSequenceTag(tag string) (name, rule, msg string) { + // Just a alias name. + // Eg: password1 + if gregex.IsMatchString(`^\w+$`, tag) { + return tag, "", "" + } + // Complete sequence tag. + // Eg: required|length:2,20|password3|same:password1#||密码强度不足|两次密码不一致 match, _ := gregex.MatchString(`\s*((\w+)\s*@){0,1}\s*([^#]+)\s*(#\s*(.*)){0,1}\s*`, tag) return strings.TrimSpace(match[2]), strings.TrimSpace(match[3]), strings.TrimSpace(match[5]) } diff --git a/util/gvalid/gvalid_check.go b/util/gvalid/gvalid_check.go index e280f54f1..59e3eadcc 100644 --- a/util/gvalid/gvalid_check.go +++ b/util/gvalid/gvalid_check.go @@ -21,7 +21,8 @@ import ( ) const ( - gSINGLE_RULE_PATTERN = `^([\w-]+):{0,1}(.*)` // 单条规则匹配正则 + // 单条规则匹配正则 + gSINGLE_RULE_PATTERN = `^([\w-]+):{0,1}(.*)` ) var ( @@ -113,6 +114,9 @@ var ( // // 3. params参数为联合校验参数,支持任意的map/struct/*struct类型,对于需要联合校验的规则有效,如:required-*、same、different; func Check(value interface{}, rules string, msgs interface{}, params ...interface{}) *Error { + if rules == "" { + return nil + } // 内部会将参数全部转换为字符串类型进行校验 val := strings.TrimSpace(gconv.String(value)) data := make(map[string]string) diff --git a/util/gvalid/gvalid_error.go b/util/gvalid/gvalid_error.go index da7bd34d9..135a3198e 100644 --- a/util/gvalid/gvalid_error.go +++ b/util/gvalid/gvalid_error.go @@ -112,6 +112,11 @@ func (e *Error) String() string { return strings.Join(e.Strings(), "; ") } +// Error implements interface of error.Error. +func (e *Error) Error() string { + return e.String() +} + // 只返回错误信息,构造成字符串数组返回 func (e *Error) Strings() (errs []string) { errs = make([]string, 0)