(快速參考)

驗證器

目的

將自訂驗證新增至欄位。

範例

even validator: {
    return (it % 2) == 0
}

// is equivalent to
even validator: { val ->
    return (val % 2) == 0
}

// Closure with two arguments, the second being the object itself
password1 validator: { val, obj ->
    obj.password2 == val
}

// Closure with three arguments, the third being the errors object
password1 validator: { val, obj, errors ->
    if (!(obj.password2 == val)) errors.rejectValue('password1', 'noMatch')
}

// Examples that pass arguments to the error message in message.properties

// Example 1: Using the "implicit" argument 0 (property name)

package demo

class Person {

    String name

    static constraints = {
        name validator: {
          if (!it) return ['entryMissing']
       }
}

// This maps to a message
// person.name.entryMissing=Please enter a name in the field {0}

// Example 2: Using the "implicit" arguments 0 (property name) and 2 (property value)
package demo

class Person {

    Integer yearOfBirth

    static constraints = {
        yearOfBirth validator: {
          if (yearOfBirth>2013) return ['yearTooBig']
       }
}

// This maps to a message
person.yearOfBirth.yearTooBig=The value {2} entered in the field {0} is not valid because it lies in the future.
// arguments are [property, class, value]. e.g. [yearOfBirth,class demo.Person, 2017]
// You will note when using this kind of message on Integers that years are displayed as 1,990 instead of 1990.
// This is addressed in the next example.

// Example 3: Much more complex
package demo

class Astronaut {

    Integer yearOfBirth
    Integer yearOfFirstSpaceTravel

    static constraints = {
        yearOfFirstSpaceTravel validator: { Integer val, Astronaut obj ->
            if (val < obj.yearOfBirth) {
                return ['datePriorTo', val.toString(), obj.yearOfBirth.toString()]
            } else if (val < (obj.yearOfBirth+18)) {
                ['maybeABitTooYoung', (val - obj.yearOfBirth)]
            }
        }
    }
}

// Respective messages
// Note that argument 3 is the property value converted toString to avoid the unwanted formatting as described before.
astronaut.yearOfFirstSpaceTravel.datePriorTo=The value {3} entered for the year of the first space travel is prior to the year of birth ({4}). Please correct the value.
// For yearOfBirth: 2017 and yearOfFirstSpaceTravel: 2012 arguments will be [yearOfFirstSpaceTravel,class demo.Astronaut,2012,2012,2017]
astronaut.yearOfFirstSpaceTravel.maybeABitTooYoung={3} years seems a bit young for travelling to space, dude!
// For yearOfBirth: 2012 and yearOfFirstSpaceTravel: 2017 arguments will be [yearOfFirstSpaceTravel,class demo.Astronaut,2017,5]

說明

自訂驗證器是由 Closure 實作,最多可接受三個參數。如果 Closure 接受零個或一個參數,參數值將會是正在驗證的參數(在零參數 Closure 的情況下為「it」)。如果它接受兩個參數,第一個為值,第二個為正在驗證的網域類別實例。當驗證需要存取其他欄位時,這很有用,例如檢查兩個輸入的密碼是否相同。如果它接受三個參數,第一個為值,第二個為實例,第三個為 Spring Errors 物件。

Closure 可以傳回

  • nulltrue(或沒有傳回值)表示值有效

  • false 表示值無效,並使用預設訊息碼

  • 字串表示要附加至 classname.propertyName 的錯誤碼。字串用於解析錯誤訊息。如果無法解析欄位特定訊息,錯誤碼本身將會被解析,允許使用全域錯誤訊息。

  • 包含上述字串的清單,然後是任何數量的引數,這些引數在 grails-app/i18n/message.properties 檔案中用作格式化訊息引數。引數的對應如下:參數 0 到 2 自動對應至 0:屬性名稱、1:類別名稱、2:屬性值。額外的參數從參數 3 開始對應。請注意,在最後的錯誤訊息中,如果在 message.properties 檔案中定義了此屬性的標籤,將會使用該標籤。否則,將會使用類別中定義的屬性名稱。

當明確傳遞錯誤碼時,通常不需要使用「return」關鍵字來傳回錯誤碼,因為如果驗證器從封閉中傳回,它將檢查是否有任何錯誤附加到錯誤物件。這可以在三參數封閉的情況下特別看到,預期錯誤物件將直接更新。