0

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

给定一个通用域,例如:

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
  }
}

然后,考虑以下Ticket 形式规则

  • 客户只能在创建时选择。在编辑时,不能显示选择,而是显示标签;
  • 产品只能在创建时选择。然而,在编辑时,选择必须显示为 disabled
  • 可以在创建或编辑时选择缺陷;
  • 无论如何都不能以这种形式设置解决方案。
  • 评论只能由具有特定角色的用户通知(例如使用 SpringSecurity)。如果用户没有这样的角色,则文本区域必须显示为禁用。

现在,我想知道的是:

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

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

    • 这种情况有什么模式或建议吗?
    • 这些规则是否应该以实际形式编写?
    • 或者表格应该“问”某人吗?也许是 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}">
    ...

等等。

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

  • 它相当冗长,因此容易出错。
  • 假设这样的规则在其他领域之间共享。在这种情况下,我必须以某种方式管理它(局部变量、复制代码等)。
  • 另外,为此,我需要在我的 TicketCommand 中有一个属性“Id”,我不知道这是否是个好主意。

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

<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 可以承担这样的责任吗?如果没有,还有其他更好的方法来集中这些复杂性吗?另外,正如我之前所说,物业客户无法设置更新。如何处理?

嗯,我想这差不多。

我很感激任何意见和建议。一些教程的任何链接也会很棒。

PS: 想看的朋友可以在github上找到完整的项目。

4

1 回答 1

1

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

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

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

我个人对命令对象结构的偏好是让它反映表单提供的数据。然后,您可以在命令对象/控制器中使用实用方法来构造/Ticket从提供的数据中获取 a。

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

形式的规则可以写在 gsp 中作为一系列条件语句。如果有大型条件语句,或者在应用程序中定期重用的任何条件语句,那么您可以将方法添加到集中它的命令对象中。

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

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

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

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

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

我相信spring security插件提供了一些标签来根据用户的角色显示/隐藏gsp元素。

为了防止用户传递无效数据,您可以使用以下代码在控制器中验证这一点。

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

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

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

于 2015-08-17T10:23:30.227 回答