Grails命令对象——有一个通用的模式吗



我想知道在grails应用程序中使用命令对象是否有任何首选模式。特别是我是否应该定义另一种保存对象的方法,或者使用相同的控制器方法来呈现表单和保存?

让我展示一个示例,其中包含用于呈现表单和保存对象的单独方法

def user(int id) {} // shows the edit user form - user.gsp. Submit takes us to saveUser method
def saveUser(UserCommand cmd) {} // actually saves the user, then redirects somewehre else

一切都应该工作,但:

如果存在验证错误,saveUser方法将不得不执行user方法所执行的整个逻辑。如果在显示用户表单之前,我们必须从数据库加载其他对象,执行一些计算等……则必须改期执行,因为我们必须再次显示表单并包含验证错误。这会导致不必要的代码重复。验证失败时,我无法重定向到user方法,因为我会丢失命令对象和与之相关的任何错误。因此,我将无法显示验证错误。

使用相同方法保存和呈现表单的另一个示例

def user(UserCommand cmd, Integer id) {
def u=User.load(id)
if (request.method=="POST"&&cmd.validate()) {
// populate u with command object values and save in database
// then redirect somewhere
}
}

此示例消除了代码重复的需要。只有当存在POST请求时,才会保存用户,在其他情况下,只显示表单。在验证错误的情况下,它们是可访问的,并且可以显示在gsp页面上。

主要问题是,即使存在对用户页面的GET请求(这意味着显示了user.gsp表单),也会创建并验证一个空的命令对象实例(因为它是在同一控制器中定义的)。因此,每次显示表单时,都会出现验证错误,即提供的值为空(因为命令对象为空)

这两种场景都可以很容易地修改以正确工作(例如:在场景1中,在会话中重定向之前保存命令对象,并且在场景2中只有POST请求时才显示验证错误),但这需要额外的代码,而且似乎不是很优雅。

对于这个问题,有没有一个更简单、更"grails"的解决方案?

下面是我在这种情况下通常会做的一个示例:

def create(MeetingCommand cmdMtn) {
switch (request.method) {
case 'GET':
cmdMtn = new MeetingCommand() 
bindData cmdMtn, params, [include: boundProperties]
createEditModel(cmdMtn: cmdMtn)
break
case 'POST':
def meetingInstance
Meeting.withTransaction { status ->
try{
Boolean okValidated = (cmdMtn.validate() && !cmdMtn.hasErrors())
if (okValidated) {
meetingInstance = meetingService.bindInstance(cmdMtn)
flashMessage 'meeting.created', FLASH_CLASS_SUCCESS, [meetingInstance.id], 'Meeting creato'  
redirect action: ACTION_SHOW, id: meetingInstance.id
}
else {
log.error cmdMtn
log.error cmdMtn.errors
status.setRollbackOnly()
createEditModel(cmdMtn: cmdMtn)
}
} catch (e){
status.setRollbackOnly()
log.error e
createEditModel(cmdMtn: cmdMtn)
}
}
break
}
}
protected createEditModel(mdl = [:]) {
mdl
}

我的灵感来自http://blog.freeside.co/post/41774629876/semi-restful-scaffolded-controllers并且域持久性逻辑已经被移动到服务层中。

问题:控制器操作中的模型填充逻辑重复。例如,在多个操作中将作者列表插入到模型中。

我有时觉得很优雅的解决方案:

def afterInterceptor = { model ->
model.authors = authorService.list()
}

查看Grails文档了解更多信息:http://grails.org/doc/latest/ref/Controllers/afterInterceptor.html

我开始向我的命令对象添加isBlankgetter:

class UserCommand {
String username
String password
boolean isBlank() { !username && !password }
}

然后在我的控制器中,我重新创建空白命令,这样就不会运行验证:

def createUser(UserCommand cmd) {
if (cmd.blank) cmd = new UserCommand()
//...render form, there will be no errors on inital view
}
def saveUser(UserCommand cmd) {
if (cmd.hasErrors()) {
return createUser(cmd)
} else {
//...and so on
}

唯一的缺点是,如果用户提交了一个空白表单,就不会有验证错误,但另一方面,很明显,空白表单必须填写内容,否则为什么会出现?

相关内容

最新更新