美文网首页Go语言
Golang Echo数据绑定中time.Time类型绑定失败

Golang Echo数据绑定中time.Time类型绑定失败

作者: 承诺一时的华丽 | 来源:发表于2018-08-03 17:06 被阅读2次

1、首先看官方绑定,time.Time将绑定失败

func(c echo.Context) (err error) {
  u := new(User)
  if err = c.Bind(u); err != nil {
    return
  }
  return c.JSON(http.StatusOK, u)
}

2、自定义绑定

加入Struct类型判断:


image.png

直接添加选项

    case reflect.Struct:
        //时间类型
        var t time.Time
        var err error
        val = strings.Replace(val, " 00:00:00", "", -1)
        
        if IsValidDate(val) {         //判断日期格式
            t, err = ParseDate(val)
            if err == nil {
                structField.Set(reflect.ValueOf(t))
            }
        } else if IsValidTime(val) {         //判断日期时间格式
            t, err = ParseTime(val)
            if err == nil {
                structField.Set(reflect.ValueOf(t))
            }
        }
        break

完整版bind.go

package handle

import (
    "reflect"
    "strconv"
    "strings"
    "github.com/labstack/echo"
    "net/http"
    "encoding/json"
    "fmt"
    "errors"
    "encoding/xml" 
    "time"
)

type CustomBinder struct{}

// Bind implements the `Binder#Bind` function.
func (b *CustomBinder) Bind(i interface{}, c echo.Context) (err error) {
    req := c.Request()
    if req.ContentLength == 0 {
        if req.Method == echo.GET || req.Method == echo.DELETE {
            if err = b.bindData(i, c.QueryParams(), "query"); err != nil {
                return echo.NewHTTPError(http.StatusBadRequest, err.Error())
            }
            return
        }
        return echo.NewHTTPError(http.StatusBadRequest, "Request body can't be empty")
    }
    ctype := req.Header.Get(echo.HeaderContentType)
    switch {
    case strings.HasPrefix(ctype, echo.MIMEApplicationJSON):
        if err = json.NewDecoder(req.Body).Decode(i); err != nil {
            if ute, ok := err.(*json.UnmarshalTypeError); ok {
                return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, field=%v, offset=%v", ute.Type, ute.Value, ute.Field, ute.Offset))
            } else if se, ok := err.(*json.SyntaxError); ok {
                return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: offset=%v, error=%v", se.Offset, se.Error()))
            } else {
                return echo.NewHTTPError(http.StatusBadRequest, err.Error())
            }
        }
    case strings.HasPrefix(ctype, echo.MIMEApplicationXML), strings.HasPrefix(ctype, echo.MIMETextXML):
        if err = xml.NewDecoder(req.Body).Decode(i); err != nil {
            if ute, ok := err.(*xml.UnsupportedTypeError); ok {
                return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error()))
            } else if se, ok := err.(*xml.SyntaxError); ok {
                return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: line=%v, error=%v", se.Line, se.Error()))
            } else {
                return echo.NewHTTPError(http.StatusBadRequest, err.Error())
            }
        }
    case strings.HasPrefix(ctype, echo.MIMEApplicationForm), strings.HasPrefix(ctype, echo.MIMEMultipartForm):
        params, err := c.FormParams()
        if err != nil {
            return echo.NewHTTPError(http.StatusBadRequest, err.Error())
        }
        if err = b.bindData(i, params, "form"); err != nil {
            return echo.NewHTTPError(http.StatusBadRequest, err.Error())
        }
    default:
        return echo.ErrUnsupportedMediaType
    }
    return
}

func (b *CustomBinder) bindData(ptr interface{}, data map[string][]string, tag string) error {
    typ := reflect.TypeOf(ptr).Elem()
    val := reflect.ValueOf(ptr).Elem()

    if typ.Kind() != reflect.Struct {
        return errors.New("binding element must be a struct")
    }

    for i := 0; i < typ.NumField(); i++ {
        typeField := typ.Field(i)
        structField := val.Field(i)
        if !structField.CanSet() {
            continue
        }
        structFieldKind := structField.Kind()
        inputFieldName := typeField.Tag.Get(tag)

        if inputFieldName == "" {
            inputFieldName = typeField.Name
            // If tag is nil, we inspect if the field is a struct.
            if _, ok := bindUnmarshaler(structField); !ok && structFieldKind == reflect.Struct {
                err := b.bindData(structField.Addr().Interface(), data, tag)
                if err != nil {
                    return err
                }
                continue
            }
        }

        inputValue, exists := data[inputFieldName]
        if !exists {
            // Go json.Unmarshal supports case insensitive binding.  However the
            // url params are bound case sensitive which is inconsistent.  To
            // fix this we must check all of the map values in a
            // case-insensitive search.
            inputFieldName = strings.ToLower(inputFieldName)
            for k, v := range data {
                if strings.ToLower(k) == inputFieldName {
                    inputValue = v
                    exists = true
                    break
                }
            }
        }

        if !exists {
            continue
        }

        // Call this first, in case we're dealing with an alias to an array type
        if ok, err := unmarshalField(typeField.Type.Kind(), inputValue[0], structField); ok {
            if err != nil {
                return err
            }
            continue
        }

        numElems := len(inputValue)
        if structFieldKind == reflect.Slice && numElems > 0 {
            sliceOf := structField.Type().Elem().Kind()
            slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
            for j := 0; j < numElems; j++ {
                if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil {
                    return err
                }
            }
            val.Field(i).Set(slice)
        } else {
            if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
                return err
            }
        }
    }
    return nil
}

func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
    // But also call it here, in case we're dealing with an array of BindUnmarshalers
    if ok, err := unmarshalField(valueKind, val, structField); ok {
        return err
    }

    switch valueKind {
    case reflect.Ptr:
        return setWithProperType(structField.Elem().Kind(), val, structField.Elem())
    case reflect.Int:
        return setIntField(val, 0, structField)
    case reflect.Int8:
        return setIntField(val, 8, structField)
    case reflect.Int16:
        return setIntField(val, 16, structField)
    case reflect.Int32:
        return setIntField(val, 32, structField)
    case reflect.Int64:
        return setIntField(val, 64, structField)
    case reflect.Uint:
        return setUintField(val, 0, structField)
    case reflect.Uint8:
        return setUintField(val, 8, structField)
    case reflect.Uint16:
        return setUintField(val, 16, structField)
    case reflect.Uint32:
        return setUintField(val, 32, structField)
    case reflect.Uint64:
        return setUintField(val, 64, structField)
    case reflect.Bool:
        return setBoolField(val, structField)
    case reflect.Float32:
        return setFloatField(val, 32, structField)
    case reflect.Float64:
        return setFloatField(val, 64, structField)
    case reflect.String:
        structField.SetString(val)
    case reflect.Struct:
        //时间类型
        var t time.Time
        var err error
        val = strings.Replace(val, " 00:00:00", "", -1)
        if IsValidDate(val) {
            t, err = ParseDate(val)
            if err == nil {
                structField.Set(reflect.ValueOf(t))
            }
        } else if IsValidTime(val) {
            t, err = ParseTime(val)
            if err == nil {
                structField.Set(reflect.ValueOf(t))
            }
        }
        break
    default:
        return errors.New("unknown type")
    }
    return nil
}

func unmarshalField(valueKind reflect.Kind, val string, field reflect.Value) (bool, error) {
    switch valueKind {
    case reflect.Ptr:
        return unmarshalFieldPtr(val, field)
    default:
        return unmarshalFieldNonPtr(val, field)
    }
}

// bindUnmarshaler attempts to unmarshal a reflect.Value into a BindUnmarshaler
func bindUnmarshaler(field reflect.Value) (echo.BindUnmarshaler, bool) {
    ptr := reflect.New(field.Type())
    if ptr.CanInterface() {
        iface := ptr.Interface()
        if unmarshaler, ok := iface.(echo.BindUnmarshaler); ok {
            return unmarshaler, ok
        }
    }
    return nil, false
}

func unmarshalFieldNonPtr(value string, field reflect.Value) (bool, error) {
    if unmarshaler, ok := bindUnmarshaler(field); ok {
        err := unmarshaler.UnmarshalParam(value)
        field.Set(reflect.ValueOf(unmarshaler).Elem())
        return true, err
    }
    return false, nil
}

func unmarshalFieldPtr(value string, field reflect.Value) (bool, error) {
    if field.IsNil() {
        // Initialize the pointer to a nil value
        field.Set(reflect.New(field.Type().Elem()))
    }
    return unmarshalFieldNonPtr(value, field.Elem())
}

func setIntField(value string, bitSize int, field reflect.Value) error {
    if value == "" {
        value = "0"
    }
    intVal, err := strconv.ParseInt(value, 10, bitSize)
    if err == nil {
        field.SetInt(intVal)
    }
    return err
}

func setUintField(value string, bitSize int, field reflect.Value) error {
    if value == "" {
        value = "0"
    }
    uintVal, err := strconv.ParseUint(value, 10, bitSize)
    if err == nil {
        field.SetUint(uintVal)
    }
    return err
}

func setBoolField(value string, field reflect.Value) error {
    if value == "" {
        value = "false"
    }
    boolVal, err := strconv.ParseBool(value)
    if err == nil {
        field.SetBool(boolVal)
    }
    return err
}

func setFloatField(value string, bitSize int, field reflect.Value) error {
    if value == "" {
        value = "0.0"
    }
    floatVal, err := strconv.ParseFloat(value, bitSize)
    if err == nil {
        field.SetFloat(floatVal)
    }
    return err
}


func ParseTime(date string) (time.Time, error) {
    date = strings.Replace(date, "/", "-", -1)
    date = strings.Replace(date, ".", "-", -1)
    return time.Parse("2006-01-02 15:04:05", date)
}

func ParseDate(date string) (time.Time, error) {
    date = strings.Replace(date, "/", "-", -1)
    date = strings.Replace(date, ".", "-", -1)
    return time.Parse("2006-01-02", date)
}



func IsValidTime(s string) bool {
    _, err := time.Parse("2006-01-02 15:04:05", s)
    if err != nil {
        return false
    }
    return true
}

func IsValidDate(s string) bool { 
    _, err := time.Parse("2006-01-02", s)
    if err != nil {
        return false
    }
    return true
}

使用示例:

if err := new(CustomBinder).Bind(user, c); err != nil {
        ...
}

相关文章

  • Golang Echo数据绑定中time.Time类型绑定失败

    1、首先看官方绑定,time.Time将绑定失败 2、自定义绑定 加入Struct类型判断: 直接添加选项 完整版...

  • 2017-07-14

    vue中,表单组件的数据双向绑定,不仅跟表单控件类型有关,还跟绑定的数据类型有关。 看来是表单控件类型去决定数据类...

  • golang时间信息的unmarshal 2022-02-15

    序列化json的时间信息如何unmarshal到golang中的time.Time类型 json的数据类型只有 s...

  • 第十三章 数据绑定

    数据绑定介绍 Spring MVC是怎样完成的数据绑定 在数据绑定过程中,Spring MVC框架会通过数据绑定组...

  • Spring MVC 数据绑定

    数据绑定 将HTTP请求中的参数绑定到Handler业务方法的形参。(传统request方式只能传String类型...

  • Vue和React数据绑定对比

    在数据绑定上来说,vue的特色是双向数据绑定,而在react中是单向数据绑定。 一 单向和双向数据绑定其实不是完全...

  • vue日常坑1

    父子通信 双向绑定时,应该直接绑定data中的数据,不要绑定props中的数据,不然会报错

  • Vue Day01

    {{}}将模型的数据显示在页面上 {{*}}首次绑定数据后,不随数据变化(绑定一次) {{{}}} 将html类型...

  • 关于 SAP UI5 OData V4 模型的类型自动识别

    除非明确指定类型,否则属性绑定会根据属性的元数据自动确定适当的类型。 例如,绑定 {DeliveryDate} 将...

  • 2019-01-15 微信小程序开发学习--页面事件和数据绑定

    数据绑定 微信小程序的数据绑定与vue相似,数据来源是通过js中的data来绑定 在js的page中定义数据,数据...

网友评论

    本文标题:Golang Echo数据绑定中time.Time类型绑定失败

    本文链接:https://www.haomeiwen.com/subject/aztyvftx.html