问题 让Grails控制器更干?


我正在寻找如何清理我的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"
    }
}

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


9797
2017-11-25 08:29


起源

请编辑问题并添加代码示例。目前问题非常模糊。 - ordnungswidrig


答案:


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

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"])
    }
}

我的getter助手是:

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

我确实认为mixin方法(非常类似于'扩展常见的抽象控制器'方式)也很好,但这种方式有两个好处:

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

在视图中我绑定到edit()我只是放了一个 id="${issue.id}" 在里面 <g:form> 它无缝地工作。


8
2017-11-25 12:26



感谢您的评论,我或多或少地将这个帖子中的一些答案结合到我身边的工作解决方案中。我使用mixin策略和一些通用的withObject方法。我将使用我现在使用的代码更新我的问题。 - Marco


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

我使用的方法是通过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
  }

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


6
2017-11-25 09:40



我并没有真正使用闭包,所以如果我犯了错误,请原谅。但是如果像'genericShow(Class clazz)'那样创建一个方法,你可以讨论这种方法吗?我一直认为一种方法不能用于咖喱而是封闭可以。 - Marco
这就是在方法名称之前使用&的原因:它将方法转换为闭包。 - Antoine
我对这个解决方案有疑问:ControllerGeneric mixin单元是否可测试?我问,因为'render'是Grails在增强控制器时提供的方法 - Stefan Schubert-Peters


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


0
2017-11-25 09:15



为什么你建议不要在这个方法名称的开头使用'get'和'set'字样? - gotomanners
避免将它们识别为“吸气剂”或“定型剂”。 - jenk