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

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

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

查看所有标签

猜你喜欢:

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

Head First Web Design

Head First Web Design

Ethan Watrall、Jeff Siarto / O’Reilly Media, Inc. / 2009-01-02 / USD 49.99

Want to know how to make your pages look beautiful, communicate your message effectively, guide visitors through your website with ease, and get everything approved by the accessibility and usability ......一起来看看 《Head First Web Design》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具