martini-contrib / binding

Martini handler for mapping and validating a raw request into a structure.
MIT License
140 stars 45 forks source link

Validation fails for Types with unexported fields #6

Closed zbindenren closed 10 years ago

zbindenren commented 10 years ago

For Json Posts I have the following Type:

type Host struct {
    Id         string    `json:"id,omitempty" gorethink:"id,omitempty"`
    Name       string    `json:"name" gorethink:"name" binding:"required"`
    Errors     bool      `json:"errors" gorethink:"errors" binding:"required"`
    Updated    time.Time `json:"updated" gorethink:"updated"`
}

The binding works perfectly. But when I set for the time.Time struct the required field tag with binding:"required I a get a panic:

type Host struct {
    Id         string    `json:"id,omitempty" gorethink:"id,omitempty"`
    Name       string    `json:"name" gorethink:"name" binding:"required"`
    Errors     bool      `json:"errors" gorethink:"errors" binding:"required"`
    Updated    time.Time `json:"updated" gorethink:"updated" binding:"required"`
}

This line is the problem:

        if strings.Index(field.Tag.Get("binding"), "required") > -1 {
            if field.Type.Kind() == reflect.Struct {
                validateStruct(errors, fieldValue) //<- THIS LINE PANICS
            } else if reflect.DeepEqual(zero, fieldValue) {
                errors.Fields[field.Name] = RequireError
            }
        }

I know the what the problem is, but not really a good, general way to solve this. The problem is that the time.Time struct does have no exported fields, just methods:

type Time struct {
     // sec gives the number of seconds elapsed since
     // January 1, year 1 00:00:00 UTC.
     sec int64

     // nsec specifies a non-negative nanosecond
     // offset within the second named by Seconds.
     // It must be in the range [0, 999999999].
     //
     // It is declared as uintptr instead of int32 or uint32
     // to avoid garbage collector aliasing in the case where
     // on a 64-bit system the int32 or uint32 field is written
     // over the low half of a pointer, creating another pointer.
     // TODO(rsc): When the garbage collector is completely
     // precise, change back to int32.
     nsec uintptr

     // loc specifies the Location that should be used to
     // determine the minute, hour, month, day, and year
     // that correspond to this Time.
     // Only the zero Time has a nil Location.
     // In that case it is interpreted to mean UTC.
     loc *Location
 }

And then the following line of code does not work:

fieldValue := val.Field(i).Interface()

Maybe someone has a good idea how to fix this.

jayrox commented 10 years ago

What is the source of the Updated field? Computer generated or User? If it is computer, you could use int64 with time.Now().Unix()

mholt commented 10 years ago

@zbindenren Is this still an issue with the latest HEAD? You've described this pretty well, I just have to make a few moments to dig in deeper. Anyone else have any idea? (I haven't used binding with time fields yet.)

mholt commented 10 years ago

Deferring until this issue re-surfaces or we hear from the opener again.