Grails:命令对象、视图模式和条件表单字段?

Grails: Command objects, view patterns and conditional form fields?

我正在尝试正确使用命令对象。我有一些问题。

给定一个通用域,例如:

package com.foo

class Ticket {

  Customer customer
  Product product
  Defect defect
  Solution solution
  String comment

  static constraints = {
    customer nullable:false
    product nullable:false
    defect nullable:true
    solution nullable:true
    comment nullable:true
  }
}

然后,考虑工单表格的以下规则

现在,我想知道的是:

  1. 使用 CommandObject 处理的最佳方法是什么 这种情况?

    • 1 个 CommandObject 用于两个操作?
    • 1 个特定于每个操作的 CommandObject?
    • 在单个 CommandObject 的情况下,如何防止用户入侵程序,例如传递禁止的参数?
  2. 实施表单规则的最佳方法是什么?也就是说,每种情况下哪个字段是shown/enabled/disabled。

    • 对于这种情况有什么模式或建议吗?
    • 这样的规则应该写成实际的形式吗?
    • 或者有人应该"ask"这种形式吗?也许是 CommandObject?还是域实例本身?

例如,考虑这个表单的要点:

<div class="fieldcontain ${hasErrors(bean:ticketInstance, field:'customer', 'error')} required">
    <label for="customer">
        <g:message code="ticket.customer.label" default="Customer" />
        <span class="required-indicator">*</span>
    </label>
    <g:if test="${ticketInstance.id}">
        <span class="label read-only">${ticketInstance.customer.name}</span>
    </g:if>
    <g:else>
        <g:select id="customer" name="customer.id" from="${Customer.list()}" optionKey="id"
                  required="" disabled="" value="${ticketInstance?.customer?.id}" class="many-to-one"/>
    </g:else>
</div>

这种情况下问题不大,因为检查比较简单。即:

<g:if test="${ticketInstance.id}">
...

但是,请考虑更复杂的规则。类似于:

<g:if test="${ticketInstance.id && currentUser.granted('SOME_RULE') && ticketInstance.someField != null}">
    ...

以此类推

现在,这种方法存在一些问题:

因此,我想知道是否有任何模式或建议可用于改善这些情况。也就是说,可以封装这种复杂性的东西。例如:

<g:if test="${cmd.customerAllowed}">
...

CommandObject 可能是这样的:

@Validateable
class TicketCreateCommand {

  def currentUser //injected somehow..      

  Customer customer
  Product product
  Defect defect
  String comment

  static constraints = {
      importFrom Ticket
  }

  boolean isNew() {
      true
  }

  boolean isCustomerAllowed() {
    this.new && currentUser.granted('SOME_RULE') && this.someField != null
    //some more rules if necessary..
  }

  boolean isSomeFieldAllowed() {
    //rules for creating
  }
}

以及用于编辑的 CommandObject:

@Validateable
class TicketEditCommand {

  def currentUser //injected somehow..      

  Customer customer
  Product product
  Defect defect
  String comment

  static constraints = {
      importFrom Ticket
  }

  boolean isNew() {
      false
  }

  boolean isCustomerAllowed() {
    this.new && currentUser.granted('SOME_RULE') && this.someField != null
    //some more rules if necessary..
  }

  boolean isSomeFieldAllowed() {
    //rules for editing
  }
}

CommndObject 可以承担这样的职责吗?如果没有,是否还有其他更好的方法来集中这些复杂性?另外,正如我之前所说,无法将 属性 客户设置为更新。如何处理?

嗯,我想差不多就是这样了。

如果有任何意见和建议,我将不胜感激。任何 link 的教程也很棒。

PS:对于那些想看的人,完整的项目可以在 github.

上找到

使用命令对象处理这种情况的最佳方法是什么?

对于查看、编辑和删除操作,需要一个简单的工单 ID。我觉得那个阶段的命令对象太过分了。

由于在更新(编辑提交操作)和保存(创建提交操作)中使用的表单背后存在逻辑,您应该尝试为每个表单创建一个命令对象。

关于命令对象的结构,我个人的偏好是让它反映表单提供的数据。然后,您可以在命令 object/controller 到 construct/get a Ticket 中使用提供的数据中的实用程序方法。

实施表单规则的最佳方法是什么?

表格的规则可以在gsp中写成一系列的条件语句。如果有大量的条件语句,或者任何在应用程序中定期重复使用的条件语句,那么您可以将方法添加到您的命令对象中以集中处理。

由于命令对象位于视图和控制器之间,我不明白为什么不能在此处存储表单逻辑。

如果应该禁用某个字段,那么您可以简单地将 disabled="disabled" 属性添加到表单字段。或者使用命令对象方法:

disabled="${cmd.isFooFieldDisabled() ? 'disabled' : ''}"

如果该字段应该隐藏,那么您可以像这样使用隐藏输入,该值对用户不可见,但会在表单提交时传递到命令对象中。

<input type="hidden" name="foo" value="${ticket.foo}"/>

我相信 are tags provided spring 安全插件根据用户的角色 show/hide gsp 元素。

为防止用户传递无效数据,您可以使用以下代码在您的控制器中对此进行验证。

def foo(FooCommand cmd)
{
    if(cmd.hasErrors())
    {
        // Handle validation errors
    }
}

方法 hasErrors 使用您定义的约束验证命令对象。从这一点开始,您可以 return 错误或从验证错误中恢复并继续操作流程。

如果用户更改隐藏表单输入,例如对象的 ID,则控制器代码应检查用户是否有权使用给定 ID 编辑对象。如果他们这样做了,那么安全策略就没有被破坏。如果不是,那么您可以 return 选择一些错误。请参阅 this post 关于隐藏字段的保护。