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). Если у пользователя нет такой роли, текстовая область должна быть отключена.
Теперь я хочу знать следующее:
Каков наилучший подход к использованию Command Object для обработки этого сценария?
- 1 единственный Command Object для обоих действий?
- 1 Command Object специфичен для каждого действия?
- В случае одного CommandObject, как запретить пользователям взломать программу, передав, например, запрещенный параметр?
Каков наилучший подход к реализации правил формы? То есть, какое поле показано / включено / отключено в каждом случае.
- Есть ли шаблон или рекомендация для этого случая?
- Должны ли такие правила быть написаны в фактической форме?
- Или форма должна "спросить" кого-то? Возможно, 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, идентификатор объекта, тогда код контроллера должен проверить, что у пользователя есть разрешение на редактирование объекта с заданным идентификатором. Если они это сделают, то политика безопасности не была нарушена. Если нет, то вы можете вернуть ошибку по вашему выбору. Смотрите этот пост о защите скрытых полей.