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)