go-macaron / binding

Package binding is a middleware that provides request data binding and validation for Macaron.
Apache License 2.0
23 stars 17 forks source link

ErrorHandler 不接受指针类型 #5

Open cgyy opened 8 years ago

cgyy commented 8 years ago

Validate 和Error 对指针的处理方式不统一, 前者接受指针类型而后者不接受

// 这样的方法不被识别 func (form VariantForm) Error(ctx macaron.Context, errs binding.Errors) {}

// macaron 识别这样的方法 func (form VariantForm) Validate(ctx macaron.Context, errs binding.Errors) binding.Errors {}

unknwon commented 8 years ago

func (form VariantForm) Error(ctx macaron.Context, errs binding.Errors) {}

不是很懂你这个方法是怎么来的。。

cgyy commented 8 years ago

你好,是这样的 比如我要自定义form的验证方法,可以这样写:

func (form *MyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
///
}

也可以这样写

func (form MyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
///
}

而自定义错误处理,只能写成这样:

func (cf ContactForm) Error(ctx *macaron.Context, errs binding.Errors) {
    // 自定义错误处理过程
}

写成下面这样不起作用

func (cf *ContactForm) Error(ctx *macaron.Context, errs binding.Errors) {
    // 自定义错误处理过程不起作用,
}
unknwon commented 8 years ago

能否发一下你绑定某个 form(不起作用的) 是怎么写的?

cgyy commented 8 years ago
// 商品型号
type VariantForm struct {
    OptionValues []int
    Price        float32 `binding:"Required"`
    Sku          string
}

// 验证选项值需要全部大于0
func (form *VariantForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
    if len(errs) > 0 {
        return errs
    }
    for _, v := range form.OptionValues {
        if v == 0 {
            errs.Add([]string{"OptionValues"}, "RequiredError", "Required")
            break
        }

    }
    return errs
}

func (form *VariantForm) Error(ctx *macaron.Context, errs binding.Errors) {
    if len(errs) > 0 {
        flash := ctx.GetVal(reflect.TypeOf(&session.Flash{})).Interface().(*session.Flash)
        errMsg := fmt.Sprintf("%s: %s", strings.Join(errs[0].FieldNames, ","), errs[0].Message)
        flash.Error(errMsg)
        ctx.Redirect(fmt.Sprintf("/variants/%d/edit", 30))
    }
}

自定义错误处理写成下面可以运行:

func (form VariantForm) Error(ctx *macaron.Context, errs binding.Errors) {
    if len(errs) > 0 {
        flash := ctx.GetVal(reflect.TypeOf(&session.Flash{})).Interface().(*session.Flash)
        errMsg := fmt.Sprintf("%s: %s", strings.Join(errs[0].FieldNames, ","), errs[0].Message)
        flash.Error(errMsg)
        ctx.Redirect(fmt.Sprintf("/variants/%d/edit", 30))
    }
}
unknwon commented 8 years ago

额。。其实我想说的是,注册路由部分的代码。。

cgyy commented 8 years ago

好的,代码是这样的:

type VariantsController struct {
    *macaron.Context
}

func MapVariants(ctx *macaron.Context) {
    ctx.Map(&VariantsController{ctx})
}

    m.Group("/variants", func() {
        m.Get("/:id/edit", (*VariantsController).Edit)
        m.Put("/:id", binding.Bind(VariantForm{}), (*VariantsController).Update)

    }, MapVariants)
unknwon commented 8 years ago

额。。看起来没问题。。我看了代码,判断 Validate 和 Error 的用法是一样的。。暂时不知道为什么。。0 0

cgyy commented 8 years ago

似乎是Bind 方法里面没有判断指针类型的原因 binding.go 104行

func Bind(obj interface{}, ifacePtr ...interface{}) macaron.Handler {
    return func(ctx *macaron.Context) {
        bind(ctx, obj, ifacePtr...)
        if handler, ok := obj.(ErrorHandler); ok {
            ctx.Invoke(handler.Error)
        } else {
            ctx.Invoke(errorHandler)
        }
    }
}

第210行

func Validate(obj interface{}) macaron.Handler {
    return func(ctx *macaron.Context) {
        var errors Errors
        v := reflect.ValueOf(obj)
        k := v.Kind()
        if k == reflect.Interface || k == reflect.Ptr {
            v = v.Elem()
            k = v.Kind()
        }
        if k == reflect.Slice || k == reflect.Array {
            for i := 0; i < v.Len(); i++ {
                e := v.Index(i).Interface()
                errors = validateStruct(errors, e)
                if validator, ok := e.(Validator); ok {
                    errors = validator.Validate(ctx, errors)
                }
            }
        } else {
            errors = validateStruct(errors, obj)
            if validator, ok := obj.(Validator); ok {
                errors = validator.Validate(ctx, errors)
            }
        }
        ctx.Map(errors)
    }
}
unknwon commented 8 years ago

噢。。那这个目前我还没想到什么好的办法,因为 Bind 是禁止你传指针的,否则会出现大量数据竞争破坏内存出现奇怪的错误