1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
15 // ValidationError simple struct to store the Message & Key of a validation error
16 type ValidationError struct {
20 // String returns the Message field of the ValidationError struct.
21 func (e *ValidationError) String() string {
28 // Validation context manages data validation and error messages.
29 type Validation struct {
30 Errors []*ValidationError
32 Translator func(locale, message string, args ...interface{}) string
36 // Keep tells revel to set a flash cookie on the client to make the validation
37 // errors available for the next request.
38 // This is helpful when redirecting the client after the validation failed.
39 // It is good practice to always redirect upon a HTTP POST request. Thus
40 // one should use this method when HTTP POST validation failed and redirect
41 // the user back to the form.
42 func (v *Validation) Keep() {
46 // Clear *all* ValidationErrors
47 func (v *Validation) Clear() {
48 v.Errors = []*ValidationError{}
51 // HasErrors returns true if there are any (ie > 0) errors. False otherwise.
52 func (v *Validation) HasErrors() bool {
53 return len(v.Errors) > 0
56 // ErrorMap returns the errors mapped by key.
57 // If there are multiple validation errors associated with a single key, the
58 // first one "wins". (Typically the first validation will be the more basic).
59 func (v *Validation) ErrorMap() map[string]*ValidationError {
60 m := map[string]*ValidationError{}
61 for _, e := range v.Errors {
62 if _, ok := m[e.Key]; !ok {
69 // Error adds an error to the validation context.
70 func (v *Validation) Error(message string, args ...interface{}) *ValidationResult {
71 result := v.ValidationResult(false).Message(message, args...)
72 v.Errors = append(v.Errors, result.Error)
76 // Error adds an error to the validation context.
77 func (v *Validation) ErrorKey(message string, args ...interface{}) *ValidationResult {
78 result := v.ValidationResult(false).MessageKey(message, args...)
79 v.Errors = append(v.Errors, result.Error)
83 // Error adds an error to the validation context.
84 func (v *Validation) ValidationResult(ok bool) *ValidationResult {
86 return &ValidationResult{Ok: ok}
88 return &ValidationResult{Ok: ok, Error: &ValidationError{}, Locale: v.Request.Locale, Translator: v.Translator}
92 // ValidationResult is returned from every validation method.
93 // It provides an indication of success, and a pointer to the Error (if any).
94 type ValidationResult struct {
95 Error *ValidationError
98 Translator func(locale, message string, args ...interface{}) string
101 // Key sets the ValidationResult's Error "key" and returns itself for chaining
102 func (r *ValidationResult) Key(key string) *ValidationResult {
109 // Message sets the error message for a ValidationResult. Returns itself to
110 // allow chaining. Allows Sprintf() type calling with multiple parameters
111 func (r *ValidationResult) Message(message string, args ...interface{}) *ValidationResult {
114 r.Error.Message = message
116 r.Error.Message = fmt.Sprintf(message, args...)
122 // Allow a message key to be passed into the validation result. The Validation has already
123 // setup the translator to translate the message key
124 func (r *ValidationResult) MessageKey(message string, args ...interface{}) *ValidationResult {
129 // If translator found, use that to create the message, otherwise call Message method
130 if r.Translator != nil {
131 r.Error.Message = r.Translator(r.Locale, message, args...)
133 r.Message(message, args...)
139 // Required tests that the argument is non-nil and non-empty (if string or list)
140 func (v *Validation) Required(obj interface{}) *ValidationResult {
141 return v.apply(Required{}, obj)
144 func (v *Validation) Min(n int, min int) *ValidationResult {
145 return v.MinFloat(float64(n), float64(min))
148 func (v *Validation) MinFloat(n float64, min float64) *ValidationResult {
149 return v.apply(Min{min}, n)
152 func (v *Validation) Max(n int, max int) *ValidationResult {
153 return v.MaxFloat(float64(n), float64(max))
156 func (v *Validation) MaxFloat(n float64, max float64) *ValidationResult {
157 return v.apply(Max{max}, n)
160 func (v *Validation) Range(n, min, max int) *ValidationResult {
161 return v.RangeFloat(float64(n), float64(min), float64(max))
164 func (v *Validation) RangeFloat(n, min, max float64) *ValidationResult {
165 return v.apply(Range{Min{min}, Max{max}}, n)
168 func (v *Validation) MinSize(obj interface{}, min int) *ValidationResult {
169 return v.apply(MinSize{min}, obj)
172 func (v *Validation) MaxSize(obj interface{}, max int) *ValidationResult {
173 return v.apply(MaxSize{max}, obj)
176 func (v *Validation) Length(obj interface{}, n int) *ValidationResult {
177 return v.apply(Length{n}, obj)
180 func (v *Validation) Match(str string, regex *regexp.Regexp) *ValidationResult {
181 return v.apply(Match{regex}, str)
184 func (v *Validation) Email(str string) *ValidationResult {
185 return v.apply(Email{Match{emailPattern}}, str)
188 func (v *Validation) IPAddr(str string, cktype ...int) *ValidationResult {
189 return v.apply(IPAddr{cktype}, str)
192 func (v *Validation) MacAddr(str string) *ValidationResult {
193 return v.apply(IPAddr{}, str)
196 func (v *Validation) Domain(str string) *ValidationResult {
197 return v.apply(Domain{}, str)
200 func (v *Validation) URL(str string) *ValidationResult {
201 return v.apply(URL{}, str)
204 func (v *Validation) PureText(str string, m int) *ValidationResult {
205 return v.apply(PureText{m}, str)
208 func (v *Validation) FilePath(str string, m int) *ValidationResult {
209 return v.apply(FilePath{m}, str)
212 func (v *Validation) apply(chk Validator, obj interface{}) *ValidationResult {
213 if chk.IsSatisfied(obj) {
214 return v.ValidationResult(true)
217 // Get the default key.
219 if pc, _, line, ok := runtime.Caller(2); ok {
220 f := runtime.FuncForPC(pc)
221 if defaultKeys, ok := DefaultValidationKeys[f.Name()]; ok {
222 key = defaultKeys[line]
225 utilLog.Error("Validation: Failed to get Caller information to look up Validation key")
228 // Add the error to the validation context.
229 err := &ValidationError{
230 Message: chk.DefaultMessage(),
233 v.Errors = append(v.Errors, err)
235 // Also return it in the result.
236 vr := v.ValidationResult(false)
241 // Check applies a group of validators to a field, in order, and return the
242 // ValidationResult from the first one that fails, or the last one that
244 func (v *Validation) Check(obj interface{}, checks ...Validator) *ValidationResult {
245 var result *ValidationResult
246 for _, check := range checks {
247 result = v.apply(check, obj)
255 // ValidationFilter revel Filter function to be hooked into the filter chain.
256 func ValidationFilter(c *Controller, fc []Filter) {
257 // If json request, we shall assume json response is intended,
258 // as such no validation cookies should be tied response
259 if c.Params != nil && c.Params.JSON != nil {
260 c.Validation = &Validation{Request: c.Request, Translator: MessageFunc}
263 errors, err := restoreValidationErrors(c.Request)
264 c.Validation = &Validation{
268 Translator: MessageFunc,
270 hasCookie := (err != http.ErrNoCookie)
274 // Add Validation errors to ViewArgs.
275 c.ViewArgs["errors"] = c.Validation.ErrorMap()
277 // Store the Validation errors
278 var errorsValue string
279 if c.Validation.keep {
280 for _, err := range c.Validation.Errors {
281 if err.Message != "" {
282 errorsValue += "\x00" + err.Key + ":" + err.Message + "\x00"
287 // When there are errors from Validation and Keep() has been called, store the
288 // values in a cookie. If there previously was a cookie but no errors, remove
290 if errorsValue != "" {
291 c.SetCookie(&http.Cookie{
292 Name: CookiePrefix + "_ERRORS",
293 Value: url.QueryEscape(errorsValue),
294 Domain: CookieDomain,
297 Secure: CookieSecure,
299 } else if hasCookie {
300 c.SetCookie(&http.Cookie{
301 Name: CookiePrefix + "_ERRORS",
303 Domain: CookieDomain,
306 Secure: CookieSecure,
312 // Restore Validation.Errors from a request.
313 func restoreValidationErrors(req *Request) ([]*ValidationError, error) {
317 errors = make([]*ValidationError, 0, 5)
319 if cookie, err = req.Cookie(CookiePrefix + "_ERRORS"); err == nil {
320 ParseKeyValueCookie(cookie.GetValue(), func(key, val string) {
321 errors = append(errors, &ValidationError{
330 // DefaultValidationKeys register default validation keys for all calls to Controller.Validation.Func().
331 // Map from (package).func => (line => name of first arg to Validation func)
332 // E.g. "myapp/controllers.helper" or "myapp/controllers.(*Application).Action"
333 // This is set on initialization in the generated main.go file.
334 var DefaultValidationKeys map[string]map[int]string