Golang Echo數(shù)據(jù)綁定中time.Time類型綁定失敗

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

直接添加選項(xiàng)

    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 {
        ...
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卵凑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子貌虾,更是在濱河造成了極大的恐慌吞加,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尽狠,死亡現(xiàn)場離奇詭異衔憨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)袄膏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進(jìn)店門践图,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沉馆,你說我怎么就攤上這事码党。” “怎么了斥黑?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵揖盘,是天一觀的道長。 經(jīng)常有香客問我锌奴,道長兽狭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任鹿蜀,我火速辦了婚禮箕慧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘茴恰。我一直安慰自己颠焦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布往枣。 她就那樣靜靜地躺著伐庭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪婉商。 梳的紋絲不亂的頭發(fā)上似忧,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天,我揣著相機(jī)與錄音丈秩,去河邊找鬼盯捌。 笑死,一個胖子當(dāng)著我的面吹牛蘑秽,可吹牛的內(nèi)容都是我干的饺著。 我是一名探鬼主播箫攀,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼幼衰!你這毒婦竟也來了靴跛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤渡嚣,失蹤者是張志新(化名)和其女友劉穎梢睛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體识椰,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绝葡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了腹鹉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片藏畅。...
    茶點(diǎn)故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖功咒,靈堂內(nèi)的尸體忽然破棺而出愉阎,到底是詐尸還是另有隱情,我是刑警寧澤力奋,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布榜旦,位于F島的核電站,受9級特大地震影響刊侯,放射性物質(zhì)發(fā)生泄漏章办。R本人自食惡果不足惜锉走,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一滨彻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挪蹭,春花似錦亭饵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至词顾,卻和暖如春八秃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肉盹。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工昔驱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人上忍。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓骤肛,卻偏偏與公主長得像纳本,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子腋颠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評論 2 349