1. Move BindXXX core logic to context.Context for two reasons: 1.1 Controller should be stateless -- Due to historical reason, it's hard for us to do this but we should try it 1.2 If users didn't use Controller to write their functions, they should be allowed to use those methods 2. Move XXXResp to context.Context
190 lines
4.6 KiB
Go
190 lines
4.6 KiB
Go
// 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
|
|
}
|