如何为play-framework编写自定义检查/验证

时间:2010-09-11 09:30:02

标签: java validation playframework

我尝试为play-framework编写检查,并看到两种不同的可能性。我描述了这两个,并想知道我的理解是否正确(所以它更像是一个教程而不是一个问题,特别是因为我没有得到任何我错过的回复)。 那么存在哪些可能性。

  1. 简单的方法:扩展班级Check
    优点:易于编写,更易于阅读 缺点:您无法对参数进行参数化,只能定义消息。
  2. 高级方式:根据OVal AbstractAnnotationCheck撰写支票 优点:您可以对检查进行参数化,并使用更简单的注释 缺点:有点复杂。
  3. 在我们查看实现之前,我想解释一下这些消息。您始终可以直接设置消息,也可以使用密钥在消息属性中引用消息。最后一个是更清洁和推荐的方式。每个验证都至少获得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,因为第一个参数始终是属性的名称。

    现在基于游戏范围检查的复杂方式: 首先,我们需要定义一个注释

    /**
     * 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-method来获取注释并可以复制参数。然后我们必须定义我们的支票。这与其他检查的实施类似。所以我们只写一个条件并返回true或false,除了一个特殊的行!如果我们使用参数化消息,我们必须在我们的方法中调用requireMessageVariablesRecreation();。 至少我们必须覆盖方法createMessageVariables。在这里,我们必须得到一个小比特play-knowlegde(所有其他的东西都被描述为here)。您将消息放入带有键和值的映射中,但播放只接受值(请参阅框架代码中的ValidCheck.java)。因此它将被位置引用。这就是我使用RangeCheck而不是TreeMap更改HashMap的实施的原因。此外,我让密钥以他们可以引用的索引开始。

    所以我希望这能让我们更清楚地知道如何为游戏编写自定义验证/检查。我希望描述是正确的。因此问题是我的理解正确吗?

1 个答案:

答案 0 :(得分:1)

至少你的第一个例子似乎在正确的路径上。您可以将它与下面提供的文档进行比较,但我会从您的示例的复杂性中假设您已经引用它。

http://www.playframework.org/documentation/1.1/validation#custom

我不太了解播放框架来评论第二个例子。