diff --git a/.gitignore b/.gitignore
index 7d98359d..9a2af3cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,4 +12,6 @@ pkg/_beeTmp2/
test/tmp/
core/config/env/pkg/
+my save path/
+
profile.out
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 699341f9..a95edd50 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -55,6 +55,7 @@
- Deprecated BeeMap and replace all usage with `sync.map` [4616](https://github.com/beego/beego/pull/4616)
- TaskManager support graceful shutdown [4635](https://github.com/beego/beego/pull/4635)
- Add comments to `web.Config`, rename `RouterXXX` to `CtrlXXX`, define `HandleFunc` [4714](https://github.com/beego/beego/pull/4714)
+- Refactor: Move `BindXXX` and `XXXResp` methods to `context.Context`. [4718](https://github.com/beego/beego/pull/4718)
- fix bug:reflect.ValueOf(nil) in getFlatParams [4715](https://github.com/beego/beego/pull/4715)
## Fix Sonar
diff --git a/go.mod b/go.mod
index 578d9f5b..a3e13fef 100644
--- a/go.mod
+++ b/go.mod
@@ -36,5 +36,7 @@ require (
go.etcd.io/etcd/client/v3 v3.5.0
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
google.golang.org/grpc v1.38.0
+ google.golang.org/protobuf v1.26.0
gopkg.in/yaml.v2 v2.4.0
+ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)
diff --git a/server/web/context/context.go b/server/web/context/context.go
index dde5e10d..f55112c8 100644
--- a/server/web/context/context.go
+++ b/server/web/context/context.go
@@ -27,24 +27,38 @@ import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
+ "encoding/json"
+ "encoding/xml"
"errors"
"fmt"
"net"
"net/http"
+ "net/url"
+ "reflect"
"strconv"
"strings"
"time"
+ "google.golang.org/protobuf/proto"
+ "gopkg.in/yaml.v3"
+
"github.com/beego/beego/v2/core/utils"
"github.com/beego/beego/v2/server/web/session"
)
// Commonly used mime-types
const (
- ApplicationJSON = "application/json"
- ApplicationXML = "application/xml"
- ApplicationYAML = "application/x-yaml"
- TextXML = "text/xml"
+ ApplicationJSON = "application/json"
+ ApplicationXML = "application/xml"
+ ApplicationForm = "application/x-www-form-urlencoded"
+ ApplicationProto = "application/x-protobuf"
+ ApplicationYAML = "application/x-yaml"
+ TextXML = "text/xml"
+
+ formatTime = "15:04:05"
+ formatDate = "2006-01-02"
+ formatDateTime = "2006-01-02 15:04:05"
+ formatDateTimeT = "2006-01-02T15:04:05"
)
// NewContext return the Context with Input and Output
@@ -65,6 +79,108 @@ type Context struct {
_xsrfToken string
}
+func (ctx *Context) Bind(obj interface{}) error {
+ ct, exist := ctx.Request.Header["Content-Type"]
+ if !exist || len(ct) == 0 {
+ return ctx.BindJSON(obj)
+ }
+ i, l := 0, len(ct[0])
+ for i < l && ct[0][i] != ';' {
+ i++
+ }
+ switch ct[0][0:i] {
+ case ApplicationJSON:
+ return ctx.BindJSON(obj)
+ case ApplicationXML, TextXML:
+ return ctx.BindXML(obj)
+ case ApplicationForm:
+ return ctx.BindForm(obj)
+ case ApplicationProto:
+ return ctx.BindProtobuf(obj.(proto.Message))
+ case ApplicationYAML:
+ return ctx.BindYAML(obj)
+ default:
+ return errors.New("Unsupported Content-Type:" + ct[0])
+ }
+}
+
+// Resp sends response based on the Accept Header
+// By default response will be in JSON
+func (ctx *Context) Resp(data interface{}) error {
+ accept := ctx.Input.Header("Accept")
+ switch accept {
+ case ApplicationYAML:
+ return ctx.YamlResp(data)
+ case ApplicationXML, TextXML:
+ return ctx.XMLResp(data)
+ case ApplicationProto:
+ return ctx.ProtoResp(data.(proto.Message))
+ default:
+ return ctx.JSONResp(data)
+ }
+}
+
+func (ctx *Context) JSONResp(data interface{}) error {
+ return ctx.Output.JSON(data, false, false)
+}
+
+func (ctx *Context) XMLResp(data interface{}) error {
+ return ctx.Output.XML(data, false)
+}
+
+func (ctx *Context) YamlResp(data interface{}) error {
+ return ctx.Output.YAML(data)
+}
+
+func (ctx *Context) ProtoResp(data proto.Message) error {
+ return ctx.Output.Proto(data)
+}
+
+// BindYAML only read data from http request body
+func (ctx *Context) BindYAML(obj interface{}) error {
+ return yaml.Unmarshal(ctx.Input.RequestBody, obj)
+}
+
+// BindForm will parse form values to struct via tag.
+func (ctx *Context) BindForm(obj interface{}) error {
+ err := ctx.Request.ParseForm()
+ if err != nil {
+ return err
+ }
+ return ParseForm(ctx.Request.Form, obj)
+}
+
+// BindJSON only read data from http request body
+func (ctx *Context) BindJSON(obj interface{}) error {
+ return json.Unmarshal(ctx.Input.RequestBody, obj)
+}
+
+// BindProtobuf only read data from http request body
+func (ctx *Context) BindProtobuf(obj proto.Message) error {
+ return proto.Unmarshal(ctx.Input.RequestBody, obj)
+}
+
+// BindXML only read data from http request body
+func (ctx *Context) BindXML(obj interface{}) error {
+ return xml.Unmarshal(ctx.Input.RequestBody, obj)
+}
+
+// ParseForm will parse form values to struct via tag.
+func ParseForm(form url.Values, obj interface{}) error {
+ objT := reflect.TypeOf(obj)
+ objV := reflect.ValueOf(obj)
+ if !isStructPtr(objT) {
+ return fmt.Errorf("%v must be a struct pointer", obj)
+ }
+ objT = objT.Elem()
+ objV = objV.Elem()
+ return parseFormToStruct(form, objT, objV)
+}
+
+func isStructPtr(t reflect.Type) bool {
+ return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
+}
+
// Reset initializes Context, BeegoInput and BeegoOutput
func (ctx *Context) Reset(rw http.ResponseWriter, r *http.Request) {
ctx.Request = r
@@ -91,7 +207,7 @@ func (ctx *Context) Abort(status int, body string) {
// WriteString writes a string to response body.
func (ctx *Context) WriteString(content string) {
- ctx.ResponseWriter.Write([]byte(content))
+ _, _ = ctx.ResponseWriter.Write([]byte(content))
}
// GetCookie gets a cookie from a request for a given key.
@@ -124,7 +240,10 @@ func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
sig := parts[2]
h := hmac.New(sha256.New, []byte(Secret))
- fmt.Fprintf(h, "%s%s", vs, timestamp)
+ _, err := fmt.Fprintf(h, "%s%s", vs, timestamp)
+ if err != nil {
+ return "", false
+ }
if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
return "", false
@@ -138,7 +257,7 @@ func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interf
vs := base64.URLEncoding.EncodeToString([]byte(value))
timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
h := hmac.New(sha256.New, []byte(Secret))
- fmt.Fprintf(h, "%s%s", vs, timestamp)
+ _, _ = fmt.Fprintf(h, "%s%s", vs, timestamp)
sig := fmt.Sprintf("%02x", h.Sum(nil))
cookie := strings.Join([]string{vs, timestamp, sig}, "|")
ctx.Output.Cookie(name, cookie, others...)
diff --git a/server/web/context/form.go b/server/web/context/form.go
new file mode 100644
index 00000000..07a1b0b2
--- /dev/null
+++ b/server/web/context/form.go
@@ -0,0 +1,189 @@
+// Copyright 2020 beego
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package context
+
+import (
+ "net/url"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+var (
+ sliceOfInts = reflect.TypeOf([]int(nil))
+ sliceOfStrings = reflect.TypeOf([]string(nil))
+)
+
+// ParseForm will parse form values to struct via tag.
+// Support for anonymous struct.
+func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) error {
+ for i := 0; i < objT.NumField(); i++ {
+ fieldV := objV.Field(i)
+ if !fieldV.CanSet() {
+ continue
+ }
+
+ fieldT := objT.Field(i)
+ if fieldT.Anonymous && fieldT.Type.Kind() == reflect.Struct {
+ err := parseFormToStruct(form, fieldT.Type, fieldV)
+ if err != nil {
+ return err
+ }
+ continue
+ }
+
+ tag, ok := formTagName(fieldT)
+ if !ok {
+ continue
+ }
+
+ value, ok := formValue(tag, form, fieldT)
+ if !ok {
+ continue
+ }
+
+ switch fieldT.Type.Kind() {
+ case reflect.Bool:
+ b, err := parseFormBoolValue(value)
+ if err != nil {
+ return err
+ }
+ fieldV.SetBool(b)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ x, err := strconv.ParseInt(value, 10, 64)
+ if err != nil {
+ return err
+ }
+ fieldV.SetInt(x)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ x, err := strconv.ParseUint(value, 10, 64)
+ if err != nil {
+ return err
+ }
+ fieldV.SetUint(x)
+ case reflect.Float32, reflect.Float64:
+ x, err := strconv.ParseFloat(value, 64)
+ if err != nil {
+ return err
+ }
+ fieldV.SetFloat(x)
+ case reflect.Interface:
+ fieldV.Set(reflect.ValueOf(value))
+ case reflect.String:
+ fieldV.SetString(value)
+ case reflect.Struct:
+ if fieldT.Type.String() == "time.Time" {
+ t, err := parseFormTime(value)
+ if err != nil {
+ return err
+ }
+ fieldV.Set(reflect.ValueOf(t))
+ }
+ case reflect.Slice:
+ if fieldT.Type == sliceOfInts {
+ formVals := form[tag]
+ fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(int(1))), len(formVals), len(formVals)))
+ for i := 0; i < len(formVals); i++ {
+ val, err := strconv.Atoi(formVals[i])
+ if err != nil {
+ return err
+ }
+ fieldV.Index(i).SetInt(int64(val))
+ }
+ } else if fieldT.Type == sliceOfStrings {
+ formVals := form[tag]
+ fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("")), len(formVals), len(formVals)))
+ for i := 0; i < len(formVals); i++ {
+ fieldV.Index(i).SetString(formVals[i])
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// nolint
+func parseFormTime(value string) (time.Time, error) {
+ var pattern string
+ if len(value) >= 25 {
+ value = value[:25]
+ pattern = time.RFC3339
+ } else if strings.HasSuffix(strings.ToUpper(value), "Z") {
+ pattern = time.RFC3339
+ } else if len(value) >= 19 {
+ if strings.Contains(value, "T") {
+ pattern = formatDateTimeT
+ } else {
+ pattern = formatDateTime
+ }
+ value = value[:19]
+ } else if len(value) >= 10 {
+ if len(value) > 10 {
+ value = value[:10]
+ }
+ pattern = formatDate
+ } else if len(value) >= 8 {
+ if len(value) > 8 {
+ value = value[:8]
+ }
+ pattern = formatTime
+ }
+ return time.ParseInLocation(pattern, value, time.Local)
+}
+
+func parseFormBoolValue(value string) (bool, error) {
+ if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" {
+ return true, nil
+ }
+ if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" {
+ return false, nil
+ }
+ return strconv.ParseBool(value)
+}
+
+// nolint
+func formTagName(fieldT reflect.StructField) (string, bool) {
+ tags := strings.Split(fieldT.Tag.Get("form"), ",")
+ var tag string
+ if len(tags) == 0 || tags[0] == "" {
+ tag = fieldT.Name
+ } else if tags[0] == "-" {
+ return "", false
+ } else {
+ tag = tags[0]
+ }
+ return tag, true
+}
+
+func formValue(tag string, form url.Values, fieldT reflect.StructField) (string, bool) {
+ formValues := form[tag]
+ var value string
+ if len(formValues) == 0 {
+ defaultValue := fieldT.Tag.Get("default")
+ if defaultValue != "" {
+ value = defaultValue
+ } else {
+ return "", false
+ }
+ }
+ if len(formValues) == 1 {
+ value = formValues[0]
+ if value == "" {
+ return "", false
+ }
+ }
+ return value, true
+}
diff --git a/server/web/context/output.go b/server/web/context/output.go
index eeac368e..0c261627 100644
--- a/server/web/context/output.go
+++ b/server/web/context/output.go
@@ -31,7 +31,8 @@ import (
"strings"
"time"
- yaml "gopkg.in/yaml.v2"
+ "google.golang.org/protobuf/proto"
+ "gopkg.in/yaml.v2"
)
// BeegoOutput does work for sending response header.
@@ -154,7 +155,7 @@ func (output *BeegoOutput) Cookie(name string, value string, others ...interface
fmt.Fprintf(&b, "; HttpOnly")
}
}
-
+
// default empty
if len(others) > 5 {
if v, ok := others[5].(string); ok && len(v) > 0 {
@@ -224,6 +225,19 @@ func (output *BeegoOutput) YAML(data interface{}) error {
return output.Body(content)
}
+// Proto writes protobuf to the response body.
+func (output *BeegoOutput) Proto(data proto.Message) error {
+ output.Header("Content-Type", "application/x-protobuf; charset=utf-8")
+ var content []byte
+ var err error
+ content, err = proto.Marshal(data)
+ if err != nil {
+ http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
+ return err
+ }
+ return output.Body(content)
+}
+
// JSONP writes jsonp to the response body.
func (output *BeegoOutput) JSONP(data interface{}, hasIndent bool) error {
output.Header("Content-Type", "application/javascript; charset=utf-8")
diff --git a/server/web/controller.go b/server/web/controller.go
index f9371f2d..6bf061dd 100644
--- a/server/web/controller.go
+++ b/server/web/controller.go
@@ -17,8 +17,6 @@ package web
import (
"bytes"
context2 "context"
- "encoding/json"
- "encoding/xml"
"errors"
"fmt"
"html/template"
@@ -32,8 +30,7 @@ import (
"strings"
"sync"
- "github.com/gogo/protobuf/proto"
- "gopkg.in/yaml.v2"
+ "google.golang.org/protobuf/proto"
"github.com/beego/beego/v2/server/web/context"
"github.com/beego/beego/v2/server/web/context/param"
@@ -244,49 +241,35 @@ func (c *Controller) HandlerFunc(fnname string) bool {
// URLMapping register the internal Controller router.
func (c *Controller) URLMapping() {}
+// Bind if the content type is form, we read data from form
+// otherwise, read data from request body
func (c *Controller) Bind(obj interface{}) error {
- ct, exist := c.Ctx.Request.Header["Content-Type"]
- if !exist || len(ct) == 0 {
- return c.BindJSON(obj)
- }
- i, l := 0, len(ct[0])
- for i < l && ct[0][i] != ';' {
- i++
- }
- switch ct[0][0:i] {
- case "application/json":
- return c.BindJSON(obj)
- case "application/xml", "text/xml":
- return c.BindXML(obj)
- case "application/x-www-form-urlencoded":
- return c.BindForm(obj)
- case "application/x-protobuf":
- return c.BindProtobuf(obj)
- case "application/x-yaml":
- return c.BindYAML(obj)
- default:
- return errors.New("Unsupported Content-Type:" + ct[0])
- }
+ return c.Ctx.Bind(obj)
}
+// BindYAML only read data from http request body
func (c *Controller) BindYAML(obj interface{}) error {
- return yaml.Unmarshal(c.Ctx.Input.RequestBody, obj)
+ return c.Ctx.BindYAML(obj)
}
+// BindForm read data from form
func (c *Controller) BindForm(obj interface{}) error {
- return c.ParseForm(obj)
+ return c.Ctx.BindForm(obj)
}
+// BindJSON only read data from http request body
func (c *Controller) BindJSON(obj interface{}) error {
- return json.Unmarshal(c.Ctx.Input.RequestBody, obj)
+ return c.Ctx.BindJSON(obj)
}
-func (c *Controller) BindProtobuf(obj interface{}) error {
- return proto.Unmarshal(c.Ctx.Input.RequestBody, obj.(proto.Message))
+// BindProtobuf only read data from http request body
+func (c *Controller) BindProtobuf(obj proto.Message) error {
+ return c.Ctx.BindProtobuf(obj)
}
+// BindXML only read data from http request body
func (c *Controller) BindXML(obj interface{}) error {
- return xml.Unmarshal(c.Ctx.Input.RequestBody, obj)
+ return c.Ctx.BindXML(obj)
}
// Mapping the method to function
@@ -440,35 +423,23 @@ func (c *Controller) URLFor(endpoint string, values ...interface{}) string {
}
func (c *Controller) JSONResp(data interface{}) error {
- c.Data["json"] = data
- return c.ServeJSON()
+ return c.Ctx.JSONResp(data)
}
func (c *Controller) XMLResp(data interface{}) error {
- c.Data["xml"] = data
- return c.ServeXML()
+ return c.Ctx.XMLResp(data)
}
func (c *Controller) YamlResp(data interface{}) error {
- c.Data["yaml"] = data
- return c.ServeYAML()
+ return c.Ctx.YamlResp(data)
}
// Resp sends response based on the Accept Header
// By default response will be in JSON
+// it's different from ServeXXX methods
+// because we don't store the data to Data field
func (c *Controller) Resp(data interface{}) error {
- accept := c.Ctx.Input.Header("Accept")
- switch accept {
- case context.ApplicationYAML:
- c.Data["yaml"] = data
- return c.ServeYAML()
- case context.ApplicationXML, context.TextXML:
- c.Data["xml"] = data
- return c.ServeXML()
- default:
- c.Data["json"] = data
- return c.ServeJSON()
- }
+ return c.Ctx.Resp(data)
}
// ServeJSON sends a json response with encoding charset.
@@ -518,11 +489,7 @@ func (c *Controller) Input() (url.Values, error) {
// ParseForm maps input data map to obj struct.
func (c *Controller) ParseForm(obj interface{}) error {
- form, err := c.Input()
- if err != nil {
- return err
- }
- return ParseForm(form, obj)
+ return c.Ctx.BindForm(obj)
}
// GetString returns the input value by key string or the default value while it's present and input is blank
diff --git a/server/web/controller_test.go b/server/web/controller_test.go
index dfd21713..6b0a5203 100644
--- a/server/web/controller_test.go
+++ b/server/web/controller_test.go
@@ -269,10 +269,10 @@ type respTestCase struct {
func TestControllerResp(t *testing.T) {
// test cases
tcs := []respTestCase{
- {Accept: context.ApplicationJSON, ExpectedContentLength: 18, ExpectedResponse: "{\n \"foo\": \"bar\"\n}"},
- {Accept: context.ApplicationXML, ExpectedContentLength: 25, ExpectedResponse: "\n bar\n"},
+ {Accept: context.ApplicationJSON, ExpectedContentLength: 13, ExpectedResponse: `{"foo":"bar"}`},
+ {Accept: context.ApplicationXML, ExpectedContentLength: 21, ExpectedResponse: `bar`},
{Accept: context.ApplicationYAML, ExpectedContentLength: 9, ExpectedResponse: "foo: bar\n"},
- {Accept: "OTHER", ExpectedContentLength: 18, ExpectedResponse: "{\n \"foo\": \"bar\"\n}"},
+ {Accept: "OTHER", ExpectedContentLength: 13, ExpectedResponse: `{"foo":"bar"}`},
}
for _, tc := range tcs {
diff --git a/server/web/templatefunc.go b/server/web/templatefunc.go
index fce1c970..c0db9990 100644
--- a/server/web/templatefunc.go
+++ b/server/web/templatefunc.go
@@ -25,13 +25,8 @@ import (
"strconv"
"strings"
"time"
-)
-const (
- formatTime = "15:04:05"
- formatDate = "2006-01-02"
- formatDateTime = "2006-01-02 15:04:05"
- formatDateTimeT = "2006-01-02T15:04:05"
+ "github.com/beego/beego/v2/server/web/context"
)
// Substr returns the substr from start to length.
@@ -266,165 +261,11 @@ func AssetsCSS(text string) template.HTML {
return template.HTML(text)
}
-// ParseForm will parse form values to struct via tag.
-// Support for anonymous struct.
-func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) error {
- for i := 0; i < objT.NumField(); i++ {
- fieldV := objV.Field(i)
- if !fieldV.CanSet() {
- continue
- }
-
- fieldT := objT.Field(i)
- if fieldT.Anonymous && fieldT.Type.Kind() == reflect.Struct {
- err := parseFormToStruct(form, fieldT.Type, fieldV)
- if err != nil {
- return err
- }
- continue
- }
-
- tags := strings.Split(fieldT.Tag.Get("form"), ",")
- var tag string
- if len(tags) == 0 || len(tags[0]) == 0 {
- tag = fieldT.Name
- } else if tags[0] == "-" {
- continue
- } else {
- tag = tags[0]
- }
-
- formValues := form[tag]
- var value string
- if len(formValues) == 0 {
- defaultValue := fieldT.Tag.Get("default")
- if defaultValue != "" {
- value = defaultValue
- } else {
- continue
- }
- }
- if len(formValues) == 1 {
- value = formValues[0]
- if value == "" {
- continue
- }
- }
-
- switch fieldT.Type.Kind() {
- case reflect.Bool:
- if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" {
- fieldV.SetBool(true)
- continue
- }
- if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" {
- fieldV.SetBool(false)
- continue
- }
- b, err := strconv.ParseBool(value)
- if err != nil {
- return err
- }
- fieldV.SetBool(b)
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- x, err := strconv.ParseInt(value, 10, 64)
- if err != nil {
- return err
- }
- fieldV.SetInt(x)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- x, err := strconv.ParseUint(value, 10, 64)
- if err != nil {
- return err
- }
- fieldV.SetUint(x)
- case reflect.Float32, reflect.Float64:
- x, err := strconv.ParseFloat(value, 64)
- if err != nil {
- return err
- }
- fieldV.SetFloat(x)
- case reflect.Interface:
- fieldV.Set(reflect.ValueOf(value))
- case reflect.String:
- fieldV.SetString(value)
- case reflect.Struct:
- switch fieldT.Type.String() {
- case "time.Time":
- var (
- t time.Time
- err error
- )
- if len(value) >= 25 {
- value = value[:25]
- t, err = time.ParseInLocation(time.RFC3339, value, time.Local)
- } else if strings.HasSuffix(strings.ToUpper(value), "Z") {
- t, err = time.ParseInLocation(time.RFC3339, value, time.Local)
- } else if len(value) >= 19 {
- if strings.Contains(value, "T") {
- value = value[:19]
- t, err = time.ParseInLocation(formatDateTimeT, value, time.Local)
- } else {
- value = value[:19]
- t, err = time.ParseInLocation(formatDateTime, value, time.Local)
- }
- } else if len(value) >= 10 {
- if len(value) > 10 {
- value = value[:10]
- }
- t, err = time.ParseInLocation(formatDate, value, time.Local)
- } else if len(value) >= 8 {
- if len(value) > 8 {
- value = value[:8]
- }
- t, err = time.ParseInLocation(formatTime, value, time.Local)
- }
- if err != nil {
- return err
- }
- fieldV.Set(reflect.ValueOf(t))
- }
- case reflect.Slice:
- if fieldT.Type == sliceOfInts {
- formVals := form[tag]
- fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(int(1))), len(formVals), len(formVals)))
- for i := 0; i < len(formVals); i++ {
- val, err := strconv.Atoi(formVals[i])
- if err != nil {
- return err
- }
- fieldV.Index(i).SetInt(int64(val))
- }
- } else if fieldT.Type == sliceOfStrings {
- formVals := form[tag]
- fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("")), len(formVals), len(formVals)))
- for i := 0; i < len(formVals); i++ {
- fieldV.Index(i).SetString(formVals[i])
- }
- }
- }
- }
- return nil
-}
-
// ParseForm will parse form values to struct via tag.
func ParseForm(form url.Values, obj interface{}) error {
- objT := reflect.TypeOf(obj)
- objV := reflect.ValueOf(obj)
- if !isStructPtr(objT) {
- return fmt.Errorf("%v must be a struct pointer", obj)
- }
- objT = objT.Elem()
- objV = objV.Elem()
-
- return parseFormToStruct(form, objT, objV)
+ return context.ParseForm(form, obj)
}
-var (
- sliceOfInts = reflect.TypeOf([]int(nil))
- sliceOfStrings = reflect.TypeOf([]string(nil))
-)
-
var unKind = map[reflect.Kind]bool{
reflect.Uintptr: true,
reflect.Complex64: true,
@@ -441,10 +282,11 @@ var unKind = map[reflect.Kind]bool{
// RenderForm will render object to form html.
// obj must be a struct pointer.
+// nolint
func RenderForm(obj interface{}) template.HTML {
objT := reflect.TypeOf(obj)
objV := reflect.ValueOf(obj)
- if !isStructPtr(objT) {
+ if objT.Kind() != reflect.Ptr || objT.Elem().Kind() != reflect.Struct {
return template.HTML("")
}
objT = objT.Elem()
@@ -549,10 +391,6 @@ func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id str
return
}
-func isStructPtr(t reflect.Type) bool {
- return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
-}
-
// go1.2 added template funcs. begin
var (
errBadComparisonType = errors.New("invalid type for comparison")