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

栏目: Go · 发布时间: 7年前

内容简介:1、首先看官方绑定,time.Time将绑定失败2、自定义绑定加入Struct类型判断:

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类型判断:

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

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类型绑定失败》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Landing Page Optimization

Landing Page Optimization

Tim Ash / Wiley Publishing / 2008-1-29 / USD 29.99

在线阅读本书 How much money are you losing because of poor landing page design? In this comprehensive, step-by-step guide, you’ll learn all the skills necessary to dramatically improve your bottom li......一起来看看 《Landing Page Optimization》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具