Я пытаюсь написать чеки для play-framework и вижу две разные возможности. Я описал оба и хочу знать, правильно ли мое понимание (так что это скорее учебник, чем вопрос, особенно потому, что я не получил ответа, что что-то пропустил)
Итак, какие возможности существуют.
- Простой способ: продление класса
Check
:
Преимущества: легче писать, легче читать
Недостатки: Вы не можете параметризовать проверку, вы можете только определить сообщение.
- Продвинутый способ: написание чека на основе OVal
AbstractAnnotationCheck
.
Преимущества: Вы можете параметризовать проверку и использовать более простую аннотацию
Недостатки: немного сложнее.
Прежде чем мы рассмотрим реализацию, я хочу объяснить сообщения. Вы всегда можете установить сообщение напрямую или использовать ключ для ссылки на сообщение в свойствах сообщения. Последний - более чистый и рекомендуемый способ. Каждая проверка получает как минимум 1 параметр: имя свойства, которое недопустимо. Таким образом, валидация или проверка конкретных параметров всегда обозначаются %i$s
, где i> 1. Формат строки сообщения должен соответствовать правилам Formatter , но я не уверен, что все функции поддерживаются. Насколько я знаю, только% s,% d и% f поддерживаются вместе с позиционированием. Так что %[argument_index$][flags]conversion
, где преобразование может быть только s, d или f.
Давайте рассмотрим два примера:
Простой способ, который я использовал в своем модуле для оптимистической блокировки:
/**
* Check with proof if the version of the current edited object is lesser
* than the version in db.
* Messagecode: optimisticLocking.modelHasChanged
* Parameter: 1 the request URL.
* Example-Message: The object was changed. <a href="%2$s">Reload</a> and do your changes again.
*
*/
static class OptimisticLockingCheck extends Check {
/**
* {@inheritDoc}
*/
@Override
public boolean isSatisfied(Object model, Object optimisiticLockingViolatedValue) {
//The comparision of version was made in the setter. Here
//we only have to check the flag.
if (((VersionedModel) model).optimisiticLockingViolated) {
final Request request = Request.current();
//The following doesn't work in 1.0 but in 1.1 see https://bugs.launchpad.net/play/+bug/634719
//http://play.lighthouseapp.com/projects/57987-play-framework/tickets/116
//setMessage(checkWithCheck.getMessage(), request != null ? request.url : "");
setMessage("optimisticLocking.modelHasChanged", request != null ? request.url : "");
}
return !((VersionedModel) model).optimisiticLockingViolated;
}
}
Вы используете этот чек с аннотацией @CheckWith(value=OptimisticLockingCheck.class, message="optimisticLocking.modelHasChanged")
Итак, давайте ближе посмотрим, как это работает. Единственное, что нам нужно сделать, - это расширить класс play.data.validation.Check и перезаписать метод isSatisfied. Там вы получите свою модель и стоимость свойств. Все, что вам нужно сделать, это вернуть true, если все в порядке или false в противном случае. В нашем случае мы хотим установить текущий URL в качестве параметра. это
может быть легко сделано с помощью вызова setMessage (). Мы даем сообщение или ключ сообщения, которые определены в свойствах сообщений и параметрах. Помните, что мы даем только 1 параметр, но обозначаем его как% 2 $ s, потому что первый параметр всегда является именем свойства.
Теперь сложный способ, основанный на Range-check of play:
Для начала нам нужно определить аннотацию
/**
* This field must be lower than and greater than.
* Message key: validation.range
* $1: field name
* $2: min reference value
* $3: max reference value
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Constraint(checkWith = RangeCheck.class)
public @interface Range {
String message() default RangeCheck.mes;
double min() default Double.MIN_VALUE;
double max() default Double.MAX_VALUE;
}
и затем чек
@SuppressWarnings("serial")
public class RangeCheck extends AbstractAnnotationCheck<Range> {
final static String mes = "validation.range";
double min;
double max;
@Override
public void configure(Range range) {
this.min = range.min();
this.max = range.max();
setMessage(range.message());
}
public boolean isSatisfied(Object validatedObject, Object value, OValContext context, Validator validator) {
requireMessageVariablesRecreation();
if (value == null) {
return true;
}
if (value instanceof String) {
try {
double v = Double.parseDouble(value.toString());
return v >= min && v <= max;
} catch (Exception e) {
return false;
}
}
if (value instanceof Number) {
try {
return ((Number) value).doubleValue() >= min && ((Number) value).doubleValue() <= max;
} catch (Exception e) {
return false;
}
}
return false;
}
@Override
public Map<String, String> createMessageVariables() {
Map<String, String> messageVariables = new TreeMap<String, String>();
messageVariables.put("2-min", Double.toString(min));
messageVariables.put("3-max", Double.toString(max));
return messageVariables;
}
}
Хорошо, я думаю, что аннотацию не нужно объяснять. Давайте посмотрим на чек. В этом случае это расширяет net.sf.oval.configuration.annotation.AbstractAnnotationCheck
. Мы должны написать метод configure, где мы получаем аннотацию и можем скопировать параметры. Тогда мы должны определить нашу проверку. Что является аналогом реализации другой проверки. Таким образом, мы только пишем наше условие и возвращаем true или false, кроме одной специальной строки! Если мы использовали параметризованное сообщение, мы должны вызвать requireMessageVariablesRecreation();
в нашем методе.
По крайней мере, мы должны переопределить метод createMessageVariables
. Здесь мы должны получить маленький бит игры-знания (все остальные вещи описаны здесь ). Вы помещаете свои сообщения в карту с ключом и значением, но игра принимает только значения (см. ValidCheck.java
в коде платформы). Так будет ссылаться на позицию. По этой причине я изменил реализацию RangeCheck
, используя TreeMap
вместо HashMap
. Кроме того, я позволю ключам начинаться с индекса, на который они могут ссылаться.
Так что я надеюсь, что это сделает более понятным, как писать собственные проверки / проверки для воспроизведения. Я надеюсь, что описание является правильным. Поэтому вопрос в моем понимании верен?