Last active
August 29, 2015 14:00
-
-
Save diatmpravin/11389823 to your computer and use it in GitHub Desktop.
validation.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package revel | |
import ( | |
"fmt" | |
"net/http" | |
"net/url" | |
"regexp" | |
"runtime" | |
) | |
// Simple struct to store the Message & Key of a validation error | |
type ValidationError struct { | |
Message, Key string | |
} | |
// String returns the Message field of the ValidationError struct. | |
func (e *ValidationError) String() string { | |
if e == nil { | |
return "" | |
} | |
return e.Message | |
} | |
// A Validation context manages data validation and error messages. | |
type Validation struct { | |
Errors []*ValidationError | |
keep bool | |
} | |
// Keep tells revel to set a flash cookie on the client to make the validation | |
// errors available for the next request. | |
// This is helpful when redirecting the client after the validation failed. | |
// It is good practice to always redirect upon a HTTP POST request. Thus | |
// one should use this method when HTTP POST validation failed and redirect | |
// the user back to the form. | |
func (v *Validation) Keep() { | |
v.keep = true | |
} | |
// Clear *all* ValidationErrors | |
func (v *Validation) Clear() { | |
v.Errors = []*ValidationError{} | |
} | |
// HasErrors returns true if there are any (ie > 0) errors. False otherwise. | |
func (v *Validation) HasErrors() bool { | |
return len(v.Errors) > 0 | |
} | |
// ErrorMap returns the errors mapped by key. | |
// If there are multiple validation errors associated with a single key, the | |
// first one "wins". (Typically the first validation will be the more basic). | |
func (v *Validation) ErrorMap() map[string]*ValidationError { | |
m := map[string]*ValidationError{} | |
for _, e := range v.Errors { | |
if _, ok := m[e.Key]; !ok { | |
m[e.Key] = e | |
} | |
} | |
return m | |
} | |
// Error adds an error to the validation context. | |
func (v *Validation) Error(message string, args ...interface{}) *ValidationResult { | |
result := (&ValidationResult{ | |
Ok: false, | |
Error: &ValidationError{}, | |
}).Message(message, args...) | |
v.Errors = append(v.Errors, result.Error) | |
return result | |
} | |
// A ValidationResult is returned from every validation method. | |
// It provides an indication of success, and a pointer to the Error (if any). | |
type ValidationResult struct { | |
Error *ValidationError | |
Ok bool | |
} | |
// Key sets the ValidationResult's Error "key" and returns itself for chaining | |
func (r *ValidationResult) Key(key string) *ValidationResult { | |
if r.Error != nil { | |
r.Error.Key = key | |
} | |
return r | |
} | |
// Message sets the error message for a ValidationResult. Returns itself to | |
// allow chaining. Allows Sprintf() type calling with multiple parameters | |
func (r *ValidationResult) Message(message string, args ...interface{}) *ValidationResult { | |
if r.Error != nil { | |
if len(args) == 0 { | |
r.Error.Message = message | |
} else { | |
r.Error.Message = fmt.Sprintf(message, args...) | |
} | |
} | |
return r | |
} | |
// Required tests that the argument is non-nil and non-empty (if string or list) | |
func (v *Validation) Required(obj interface{}) *ValidationResult { | |
fmt.Println("<<-----------------REQUIRED--------------------------------->>") | |
fmt.Printf("Here is obj---->", obj) | |
fmt.Println("Here is required---->", Required{}) | |
return v.apply(Required{}, obj) | |
} | |
func (v *Validation) Min(n int, min int) *ValidationResult { | |
return v.apply(Min{min}, n) | |
} | |
func (v *Validation) Max(n int, max int) *ValidationResult { | |
return v.apply(Max{max}, n) | |
} | |
func (v *Validation) Range(n, min, max int) *ValidationResult { | |
return v.apply(Range{Min{min}, Max{max}}, n) | |
} | |
func (v *Validation) MinSize(obj interface{}, min int) *ValidationResult { | |
return v.apply(MinSize{min}, obj) | |
} | |
func (v *Validation) MaxSize(obj interface{}, max int) *ValidationResult { | |
return v.apply(MaxSize{max}, obj) | |
} | |
func (v *Validation) Length(obj interface{}, n int) *ValidationResult { | |
return v.apply(Length{n}, obj) | |
} | |
func (v *Validation) Match(str string, regex *regexp.Regexp) *ValidationResult { | |
return v.apply(Match{regex}, str) | |
} | |
func (v *Validation) Email(str string) *ValidationResult { | |
return v.apply(Email{Match{emailPattern}}, str) | |
} | |
func (v *Validation) apply(chk Validator, obj interface{}) *ValidationResult { | |
fmt.Println("<<---------------------APPLY----------------------------->>") | |
fmt.Println("required to validation obj-----chk---->", chk) | |
fmt.Println("Object again as a validation-----obj---->", obj) | |
if chk.IsSatisfied(obj) { | |
return &ValidationResult{Ok: true} | |
} | |
// Get the default key. | |
var key string | |
if pc, _, line, ok := runtime.Caller(2); ok { | |
f := runtime.FuncForPC(pc) | |
if defaultKeys, ok := DefaultValidationKeys[f.Name()]; ok { | |
key = defaultKeys[line] | |
} | |
} else { | |
INFO.Println("Failed to get Caller information to look up Validation key") | |
} | |
// Add the error to the validation context. | |
err := &ValidationError{ | |
Message: chk.DefaultMessage(), | |
Key: key, | |
} | |
v.Errors = append(v.Errors, err) | |
// Also return it in the result. | |
return &ValidationResult{ | |
Ok: false, | |
Error: err, | |
} | |
} | |
// Apply a group of validators to a field, in order, and return the | |
// ValidationResult from the first one that fails, or the last one that | |
// succeeds. | |
func (v *Validation) Check(obj interface{}, checks ...Validator) *ValidationResult { | |
var result *ValidationResult | |
for _, check := range checks { | |
result = v.apply(check, obj) | |
if !result.Ok { | |
return result | |
} | |
} | |
return result | |
} | |
// Revel Filter function to be hooked into the filter chain. | |
func ValidationFilter(c *Controller, fc []Filter) { | |
errors, err := restoreValidationErrors(c.Request.Request) | |
c.Validation = &Validation{ | |
Errors: errors, | |
keep: false, | |
} | |
hasCookie := (err != http.ErrNoCookie) | |
fc[0](c, fc[1:]) | |
// Add Validation errors to RenderArgs. | |
c.RenderArgs["errors"] = c.Validation.ErrorMap() | |
// Store the Validation errors | |
var errorsValue string | |
if c.Validation.keep { | |
for _, error := range c.Validation.Errors { | |
if error.Message != "" { | |
errorsValue += "\x00" + error.Key + ":" + error.Message + "\x00" | |
} | |
} | |
} | |
// When there are errors from Validation and Keep() has been called, store the | |
// values in a cookie. If there previously was a cookie but no errors, remove | |
// the cookie. | |
if errorsValue != "" { | |
c.SetCookie(&http.Cookie{ | |
Name: CookiePrefix + "_ERRORS", | |
Value: url.QueryEscape(errorsValue), | |
Path: "/", | |
HttpOnly: CookieHttpOnly, | |
Secure: CookieSecure, | |
}) | |
} else if hasCookie { | |
c.SetCookie(&http.Cookie{ | |
Name: CookiePrefix + "_ERRORS", | |
MaxAge: -1, | |
Path: "/", | |
HttpOnly: CookieHttpOnly, | |
Secure: CookieSecure, | |
}) | |
} | |
} | |
// Restore Validation.Errors from a request. | |
func restoreValidationErrors(req *http.Request) ([]*ValidationError, error) { | |
var ( | |
err error | |
cookie *http.Cookie | |
errors = make([]*ValidationError, 0, 5) | |
) | |
if cookie, err = req.Cookie(CookiePrefix + "_ERRORS"); err == nil { | |
ParseKeyValueCookie(cookie.Value, func(key, val string) { | |
errors = append(errors, &ValidationError{ | |
Key: key, | |
Message: val, | |
}) | |
}) | |
} | |
return errors, err | |
} | |
// Register default validation keys for all calls to Controller.Validation.Func(). | |
// Map from (package).func => (line => name of first arg to Validation func) | |
// E.g. "myapp/controllers.helper" or "myapp/controllers.(*Application).Action" | |
// This is set on initialization in the generated main.go file. | |
var DefaultValidationKeys map[string]map[int]string |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment