
Grails: revalidation in Command Objects
Command objects в Grails — очень полезные штуки. Это как бы domain objects, но не имеющие связи с базой данных. Зато подвергающиеся валидации. Это классно, если в приложении есть формы, которые должны проверять входные данные, но не хранить их в базе (формы логинов, отправки сообщений, да вообще просто формы).
Но есть у них одно неприятное свойство. Они так устроены, что цепляются к методу контроллера и принимают данные автоматически — то есть, в момент инициализации контроллера данные из запроса автоматически биндятся в комманд обжект и проходят первую стадию валидации. Соответственно, если в CO определены свойства таких типов, которых не может быть в параметрах запроса — например, Date особенно если дата в форме собирается из нескольких полей (типа даты, часов и минут). То есть, по хорошему мы должны бы иметь такой код:
class SomeController {
def index = {MyCommand cmd ->
def date = ... // код, который собирает дату из разных полей
cmd.date = date
if(!cmd.hasErrors()){
}
}
}
Но такая тема не пройдет, потому что во время выполнения кода метода объект cmd уже содержит в себе ошибку биндинга и очень даже hasErrors. А вот способа повторной валидации данных Grails не предлагает.
Что же можно сделать? Можно сделать плагин, который вполне может решить эту проблему. Просто создадим соответствующий метод и назначим его всем Command Objects. Код, который занимается биндингом копипастим с небольшими изменениями из ControllerGrailsPlugin:
import org.codehaus.groovy.grails.web.metaclass.*
import org.springframework.web.context.request.RequestContextHolder as RCH
public class SomeGrailsPlugin{
def version = "0.1"
def watchedResources = [
"file:./grails-app/controllers/**/*Controller.groovy",
"file:./plugins/*/grails-app/controllers/**/*Controller.groovy"
]
def doWithDynamicMethods = { applicationContext ->
createRevalidateMethod(application, applicationContext)
}
def onChange = {event ->
println 'change catched in some!'
createRevalidateMethod(application, event.ctx)
}
def createRevalidateMethod(application, applicationContext){
def bind = new BindDynamicMethod()
application.controllerClasses.each {controller ->
def commandObjectClasses = controller.commandObjectClasses
commandObjectClasses.each {commandObjectClass ->
commandObjectClass.metaClass.revalidate = {
def commandObject = delegate
def params = RCH.currentRequestAttributes().params
bind.invoke(commandObject, "bindData", [commandObject, params] as Object[])
def errors = commandObject.errors ?: new BindException(commandObject, paramType.name)
def constrainedProperties = commandObject.constraints?.values()
constrainedProperties.each {constrainedProperty ->
constrainedProperty.messageSource = applicationContext.getBean("messageSource")
constrainedProperty.validate(commandObject, commandObject.getProperty(constrainedProperty.getPropertyName()), errors);
}
commandObject.errors = errors
}
}
}
}
}
И изменяем наш контроллер:
class SomeController {
def index = {MyCommand cmd ->
def date = ... // код, который собирает дату из разных полей
cmd.date = date
cmd.revalidate()
if(!cmd.hasErrors()){
}
}
}
Ну вот, теперь стало значительно легче. Энджой!
Комментарии (0)