让 Grails 控制器更干燥?

Posted

技术标签:

【中文标题】让 Grails 控制器更干燥?【英文标题】:Making Grails controllers more DRY? 【发布时间】:2011-11-25 08:29:40 【问题描述】:

我正在寻找如何清理我的 Grails 控制器代码的方法。在各种控制器中,我或多或少具有相同的逻辑..

获取对象 检查是否存在 等等..

有没有建议的方法让控制器动作重用通用代码?

--- 解决方案---

问题的所有答案都有助于我们实施的解决方案。

我们使用 Mixin 方法创建了一个在我们的控制器中使用的类。 mixin 公开的方法之一是 withObject 方法。此方法从控制器获取域名并将其用作该方法的基础。当然可以覆盖此行为!

def withObject(object=this.getClass().getName()-"Controller", id="id", Closure c) 
    assert object
    def obj =  grailsApplication.classLoader.loadClass(object).get(params[id])
    if(obj) 
        c.call obj
     else 
        flash.message = "The object was not found"
        redirect action: "list"
    

所以所有答案都有助于解决方案!非常感谢!

【问题讨论】:

请编辑问题并添加您的代码示例。目前这个问题很模糊。 【参考方案1】:

当这个问题出现时,我总是会拿出这篇博文:

http://mrpaulwoods.wordpress.com/2011/01/23/a-pattern-to-simplify-grails-controllers/

基本上,您的控制器中的各个域都有一个私有助手。

private def withPerson(id="id", Closure c) 
    def person = Person.get(params[id])
    if(person) 
        c.call person
     else 
        flash.message = "The person was not found."
        redirect action:"list"
    

您对 getter 进行编码的方式非常灵活,我的一个典型用途(在博客中没有介绍)是用于编辑等。

我通常是这样编码的(我喜欢这种模式的清晰划分和可读性):

 def editIssue() 
    withIssue  Issue issue ->
        def issueTypes = IssueTypeEnum.values().collect it.text 
        [issueTypes:issueTypes,activePage:"issue", issue: issue]
    


 def doEditIssue(IssueCommand cmd) 
    if(cmd.validate()) 
        withIssue  Issue issue ->
            issue.updateIssue(cmd)
            redirect(action: "show", id: issue.id)
        
    
    else 
        def issueTypes = IssueTypeEnum.values().collect it.text 
        render(view: "edit", model:[issueTypes:issueTypes,issue:cmd,activePage:"issue"])
    

我的吸气剂助手是:

private def withIssue( Closure c) 
    def issue = Issue.get(params.id)
    if(issue) 
        c.call issue
    
    else 
        response.sendError(404)
    

我确实认为 mixin 方法(非常类似于“扩展通用抽象控制器”的方式)也不错,但这种方式有两个优点:

    您可以键入帮助程序,就像您在闭包中看到的那样,让您可以访问 STS/IDEA 中的方法等(未经测试的 Netbeans) 重复性不是很高,并且能够改变getter(例如使用BarDomain.findByFoo(params.id)等)

在我绑定到 edit() 的视图中,我只是在 <g:form> 中添加了一个 id="$issue.id",它可以无缝运行。

【讨论】:

感谢您的 cmets,我或多或少地将这个线程中的一些答案与我身边的工作解决方案结合在一起。我将 mixin 策略与某种通用的 withObject 方法一起使用。我将使用我现在使用的代码更新我的问题。【参考方案2】:

我不推荐继承,因为你不能在几个超类中传播泛型方法。如果你有很多控制器,你的抽象类很快就会变得混乱。您不能使用组合(例如使用服务),因为您无法直接从那里访问 responserenderparams

我使用的方法是通过 Mixins 注入泛型方法。

@Mixin(ControllerGenericActions)
@Mixin(ControllerUtil)
class BookController 
  def show = &genericShow.curry(Book)

  def exists = 
    render(idExists(Book))
  

第一个动作show 使用ControllerGenericActions.groovy 中的泛型方法,并绑定了一个参数。 mixin idExists 方法的第二个用途是在控制器操作中。

这是src/groovy/ControllerGenericActions.groovy的示例代码

class ControllerGeneric 
  def genericShow(Class clazz) 
    render clazz.get(params.id) as XML
  

src/groovy/ControllerUtil.groovy

class ControllerUtil 
  def idExists (Class clazz) 
    return clazz.get(params.id) != null
  

在这种情况下不是很有用,但你明白了。

【讨论】:

我不太喜欢使用闭包,所以如果我犯了错误,请原谅。但是,如果创建了像“genericShow(Class clazz)”这样的方法,你可以使用该方法吗?我一直认为方法不能被柯里化,但闭包可以。 这就是在方法名前使用&的原因:它把方法变成了闭包。 我有一个关于这个解决方案的问题:ControllerGeneric mixin 单元是否可测试?我问是因为“渲染”是 Grails 在增强控制器时提供的一种方法【参考方案3】:

使用通用方法(使用“protected”指令)实现抽象控制器,并从中扩展您的真实控制器。不要在此方法名称的开头使用 'get' 和 'set' 词。不好,但它有效。

【讨论】:

为什么不建议在该方法的名称开头使用“get”和“set”字眼? 避免将它们识别为“getter”或“setter”。

以上是关于让 Grails 控制器更干燥?的主要内容,如果未能解决你的问题,请参考以下文章

从 Grails 中的控制器渲染 json 视图

Grails:在控制器中设置啥以达到 URL 映射中的错误处理?

如何在 grails 中实现请求超时?

rails:保持干燥,达到什么水平? [关闭]

Grails 和 Oracle Anydata

如何在 Grails 中实现超时页面