Grails: командные объекты, шаблоны представлений и поля условных форм?

Я пытаюсь правильно использовать объекты Command. И у меня есть несколько вопросов.

Для общего домена, например:

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

Затем рассмотрите следующие правила формы Билета:

  • Заказчик может быть выбран только при создании. При редактировании выбор не должен отображаться, вместо этого отображается метка;
  • Товар можно выбрать только при создании. Тем не менее, при редактировании выбор должен быть отключен;
  • Дефект может быть выбран либо при создании, либо при редактировании;
  • Решение не может быть установлено в этой форме вообще.
  • Комментировать могут только пользователи с определенной ролью (например, с помощью SpringSecurity). Если у пользователя нет такой роли, текстовая область должна быть отключена.

Теперь я хочу знать следующее:

  1. Каков наилучший подход к использованию Command Object для обработки этого сценария?

    • 1 единственный Command Object для обоих действий?
    • 1 Command Object специфичен для каждого действия?
    • В случае одного CommandObject, как запретить пользователям взломать программу, передав, например, запрещенный параметр?
  2. Каков наилучший подход к реализации правил формы? То есть, какое поле показано / включено / отключено в каждом случае.

    • Есть ли шаблон или рекомендация для этого случая?
    • Должны ли такие правила быть написаны в фактической форме?
    • Или форма должна "спросить" кого-то? Возможно, Command Object? Или сам экземпляр Домена?

Например, рассмотрим суть этой формы:

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

И так далее.

Теперь есть несколько проблем с этим подходом:

  • Это довольно многословно, и, следовательно, склонны к ошибкам.
  • Предположим, что такое правило было распространено среди других областей. В этом случае мне придется как-то управлять (локальной переменной, дублировать код и т. Д.).
  • И плюс, для этого мне понадобится свойство 'Id' в моем TicketCommand, которое я не знаю, если это хорошая идея.

И поэтому мне было интересно, есть ли какой-либо шаблон или рекомендация, которая может быть использована для улучшения этих сценариев. То есть что-то, что могло бы заключать в себе такую ​​сложность. Например:

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

Где Command Object может быть чем-то вроде строки:

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

И Command Object для редактирования:

@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.

1 ответ

Решение

Каков наилучший подход к использованию объекта команды для обработки этого сценария?

Для действий "Просмотр, редактирование и удаление" потребуется простой идентификатор заявки. Я чувствую, что командные объекты на этом этапе излишни.

Поскольку за формами, используемыми в "Обновлении" (изменение действия отправки) и "Сохранить" (создании действия отправки), есть логика, вам следует попробовать создать объект команды для каждого из них.

Мое личное предпочтение относительно структуры командного объекта состоит в том, чтобы он отражал данные, предоставленные формой. После этого вы можете использовать служебный метод в объекте / контроллере команды для создания / получения Ticket из предоставленных данных.

Каков наилучший подход к реализации правил формы?

Правила для формы могут быть записаны в gsp как серия условных операторов. Если есть большие условные операторы или те, которые регулярно используются в приложении, вы можете добавить методы к объекту команды, которые централизуют это.

Поскольку объект команды лежит между представлением и контроллерами, я не понимаю, почему логика формы не может быть сохранена здесь.

Если поле должно быть отключено, вы можете тривиально добавить disabled="disabled" атрибут для поля формы. Или используя метод объекта команды:

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

Если поле должно быть скрыто, то вы можете использовать скрытый ввод, например, так, что это значение не будет видимо пользователю, но будет передано в объект команды при отправке формы.

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

Я полагаю, что в весеннем плагине безопасности есть теги для отображения / скрытия элементов gsp в зависимости от роли пользователя.

Чтобы предотвратить передачу пользователями недопустимых данных, вы можете проверить это в своем контроллере, используя следующий код.

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

Метод hasErrors проверяет объект команды, используя определенные вами ограничения. С этого момента вы можете либо вернуть ошибку, либо восстановиться после ошибки проверки и продолжить выполнение последовательности действий.

Если пользователь изменяет ввод скрытой формы, например, EG, идентификатор объекта, тогда код контроллера должен проверить, что у пользователя есть разрешение на редактирование объекта с заданным идентификатором. Если они это сделают, то политика безопасности не была нарушена. Если нет, то вы можете вернуть ошибку по вашему выбору. Смотрите этот пост о защите скрытых полей.

Другие вопросы по тегам