05.用play验证http数据

本文介绍Play框架中HTTP数据验证的方法,包括控制器方法验证、使用注释声明验证、验证复杂对象及自定义验证等。同时提供了多种显示错误信息的方式。

05.用play验证http数据

验证确保了某些指定的需求能够获得正确的值。一般用于在存入数据库前对数据进行验证或表单验证。

在play里验证如何进行的?

每个请求都有他自己的Validation(验证)对象和相应的错误集合。有以下三种方式来定义验证。

  1. 在一个控制器方法,可以直接调用控制器的validation属性的方法。也可使用play.data.validation.Validation类的静态方法来访问API子集。
  2. 在控制器的方法参数上使用注释声明来进行验证。
  3. 为一个action方法的POJO参数添加@Valid注释来验证POJO属性。

验证对象负责维护play.data.validation.Error对象集合。每个error都有两个属性:

  • key:用于确定是哪个元素导致的错误。当play发生错误时,key值可以任意设置,它遵循java变量默认命名约定。
  • message:其内容包含了错误的文本描述。message可以是纯文本消息,也可以是key的消息绑定(特别是国际化支持)。

下面我们使用第一种方式来验证一个简单的http参数:

public static void hello(String name) {

    validation.required(name);

     …

}

此代码用于检测name变量是否正确设置。如果不正确,相应的错误消息将会增加到当前错误集合里。

如果需要,可以为每一个需要验证的变更重复这个操作:

public static void hello(String name, Integer age) {

    validation.required(name);

    validation.required(age);

    validation.min(age, 0);

     …

}

验证的错误消息

最后我们可以检索出错误信息并显示出来:

public static void hello(String name, Integer age) {

    validation.required(name);

    validation.required(age);

    validation.min(age, 0);

    

    if(validation.hasErrors()) {

         for(Errorerror : validation.errors()) {

            System.out.println(error.message());

         }

     }

}

假如name和age都为null,这时将显示:

Required

Required

这是因为在$PLAY_HOME/resources/messages里设置的默认消息如下:

validation.required=Required

可通过以下三种方式定制验证消息:

  1. 重写应用程序的messages文件,对其中的消息进行重新定义。
  2. 提供一个定制消息作为附加的验证参数。
  3. 为局部消息提供一个消息key作为附加的验证参数。

Localised validation messages 局部验证消息

最简单的方式就是重写messages文件内容:

validation.required = Please enter a value

也可提供其他语言的区位,详见Internationalization

验证消息参数

在消息里为错误key使用占位符:

validation.required=%s is required

输出为:

name is required

age is required

缺点:当超过一个必须字体验证时(使用validation.requied(age)),play会发生不能确定当前的参数名称语法错误。在这种情况下,就必须直接指定域名称,比如:validation.required("age", age)。

错误key默认就是参数名称。比如在hello action里的name参数可以限定如下:

name = Customer name

输出为:

Customer name is required

age is required

也可使用error.message(String key)方法重载错误key:

Error error = validation.required(name).error;

if(error != null) {

   System.out.println(error.message("Customer name"));

}

许多内建的验证定义附加消息参数都适用于验证参数。比如,match验证为指定的正则表达式定义了第2个字符串参数,这与%s占位符不同:

validation.match=Must match %2$s

与此相似,range验证定义了两个附加的数字参数2和3:

validation.range=Not in the range %2$d through %3$d

查看一下$PLAY_HOME/resources/messages文件也了解更多的验证参数。

定制局部验证消息

$PLAY_HOME/resources/messages使用默认的key为每个play内建的验证定义了验证信息。你可以指定不同的消息key,比如:

validation.required.em = You must enter the %s!

为消息使用新的消息key,就是为了手工在action方法里进行验证:

validation.required(manualKey).message("validation.required.em");

另外一种方式就是在注释的message参数里使用key:

public static void hello(@Required(message="validation.required.em")String name) {

   …

}

使用同样的技术可以用于验证JavaBean的属性:

public static void hello(@Valid Person person) {

   …

}

 

public class Person extends Model {

  @Required(message = "validation.required.emphasis")

   public String name;

   …

}

定制teral(非局部)验证消息

如果没有为key定义错误消息,那么play只返回消息的key名称,也就是说只需使用文字消息代替消息key。上面的示例可修改为:

validation.required(manualKey).message("Give us aname!");

action方法参数注释:

public static void save(@Required(message = "Give usa name!") String name) {

   …

}

JavaBean属性注释:

public static void save(@Valid Person person) {

   …

}

 

public class Person extends Model {

  @Required(message = "Give us a name!")

   public Stringname;

   …

}

在模板里显示验证错误消息

很多情况下都需要在视图模板里显示错误消息。在模板里使用errors对象可以访问这些错误消息,一些标签专用于显示错误消息:

示例:

public static void hello(String name, Integer age) {

  validation.required(name);

  validation.required(age);

  validation.min(age, 0);

   render(name,age);

}

模板代码:

#{ifErrors}

 

  <h1>Oops…</h1>

 

   #{errors}

      <li>${error}</li>

   #{/errors}

 

#{/ifErrors}

#{else}

 

   Hello ${name},you are ${age}.

 

#{/else}

在真实的应用程序里可能需要显示真实的窗体,因此就需要使用两个action:一个用于显示窗体,另一个用于处理POST。

当然,第二个action用于验证操作,当错误发生时就必须直接转向第一个Action。在这种情况下,在跳转期间就需要一个特定的技巧来保存错误消息。使用validation.keep()方法将为下一个action保存错误集合。

示例:

public class Application extends Controller {

 

   public staticvoid index() {

      render();

   }

 

   public staticvoid hello(String name, Integer age) {

     validation.required(name);

     validation.required(age);

     validation.min(age, 0);

      if(validation.hasErrors()){

         params.flash(); //把http参数添加到flash作用域

         validation.keep(); // 为下一个请求保存错误消息

          index();

      }

      render(name,age);

   }

 

}

view/Application/index.html模板代码:

#{ifErrors}

  <h1>Oops…</h1>

 

   #{errors}

      <li>${error}</li>

   #{/errors}

#{/ifErrors}

 

#{form @Application.hello()}

   <div>

      Name:<input type="text" name="name"value="${flash.name}" />

   </div>

   <div>

      Age:<input type="text" name="age"value="${flash.age}" />

   </div>

   <div>

      <inputtype="submit" value="Say hello" />

   </div>

#{/form}

当错误发生时,为每个域显示错误消息以获得更好的用户体验:

#{ifErrors}

  <h1>Oops…</h1>

#{/ifErrors}

 

#{form @Application.hello()}

   <div>

      Name:<input type="text" name="name"value="${flash.name}" />

      <spanclass="error">#{error 'name' /}</span>

   </div>

   <div>

      Age:<input type="text" name="age"value="${flash.age}" />

      <spanclass="error">#{error 'age' /}</span>

   </div>

   <div>

      <inputtype="submit" value="Say hello" />

   </div>

#{/form}

验证注释

在play.data.validation包里提供的注释语句来给每个Validation对象的方法进行注释进行强制验证,更为简洁。使用验证注释,只需要为控制器方法的参数进行注释即可:

public static void hello(@Required String name, @Required@Min(0) Integer age) {

  if(validation.hasErrors()) {

      params.flash(); //把http参数添加到flash作用域

      validation.keep(); //为下一个请求保存错误消息

       index();

   }

   render(name,age);

}

验证复杂对象

使用验证注释,可以应用于模型对象的属性,之后在控制器里所有的属性都必须是有效的。让我们重写一下User类。

对User类的属性使用验证注释:

package models;

 

public class User {

   

    @Required

    public Stringname;

 

    @Required

    @Min(0)

    public Integerage;

}

修改hello方法,使用 @Valid注释来指定所有的User对象都必须是有效的:

public static void hello(@Valid User user) {

  if(validation.hasErrors()) {

      params.flash(); //把http参数添加到flash作用域

      validation.keep(); //为下一个请求保存错误消息

       index();

   }

   render(name,age);

}

修改后的窗体代码:

#{ifErrors}

   <h1>Oops…</h1>

#{/ifErrors}

 

#{form @Application.hello()}

   <div>

      Name:<input type="text" name="user.name"value="${flash['user.name']}" />

      <spanclass="error">#{error 'user.name' /}</span>

   </div>

   <div>

      Age:<input type="text" name="user.age"value="${flash['user.age']}" />

      <spanclass="error">#{error 'user.age' /}</span>

   </div>

   <div>

      <inputtype="submit" value="Say hello" />

   </div>

#{/form}

内建验证

play.data.validation 包包含了许多内建的验证built-in validations,即可以使用Validation对象,也可使用注释。

使用@CheckWith定制验证

使用@CheckWith注释可以用来绑定自己Check验证实现。

示例:

public class User {

   

    @Required

   @CheckWith(MyPasswordCheck.class)

    public Stringpassword;

   

    static classMyPasswordCheck extends Check {

       

        publicboolean isSatisfied(Object user, Object password) {

            returnnotMatchPreviousPasswords(password);

        }

    }

}

默认的验证错误消息key是validation.invalid,要使用不同的key,需要调用Check.setMessage方法(带一个消息key和一个消息参数)。

static class MyPasswordCheck extends Check {

 

    public booleanisSatisfied(Object user, Object password) {

        final DatelastUsed = dateLastUsed(password);

       setMessage("validation.used", JavaExtensions.format(lastUsed));

        returnlastUsed == null;

    }

}

消息总是查找第一个参数的名称作为field名称,把随后的消息参数作为子参数,因此,上面的示例可以定义如下:

validation.used = &{%1$s} already used on date %2$s

user.password = Password

当&{%1$s}使用第1个位置(field名称)作为消息key来进行消息查找,%2$s作为该key的值(格式化后的日期)。

消息语法- %s, %s2$s and &{…}详见Retrieve localized messages

定制注释

也可定制自己的验证注释,虽然要复杂一点,但可以使模型代码更简洁,而且可以引入验证参数。

比如,假定我们打算对URL进行验证(使用@URL),通过带参确定指定的URL允许通过。

首先,需要重写默认的消息,定制一个带参的验证注释:

import net.sf.oval.configuration.annotation.Constraint;

import java.lang.annotation.*;

 

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.FIELD, ElementType.PARAMETER})

@Constraint(checkWith = URICheck.class)

public @interface URI {

    String message()default URICheck.message;

}

这个注释参考net.sf.oval.configuration.annotation.AbstractAnnotationCheck实现。

public class URICheck extendsAbstractAnnotationCheck<URI> {

 

    /** Errormessage key. */

    public finalstatic String message = "validation.uri";

 

    /** URI schemes allowed by validation. */

    privateList<String> schemes;

 

    @Override

    public voidconfigure(URI uri) {

       setMessage(uri.message());

       this.schemes = Arrays.asList(uri.schemes());

    }

 

    /**

     * Add the URIschemes to the message variables so they can be included

     * in the errormessage.

     */

    @Override

    publicMap<String, String> createMessageVariables() {

        finalMap<String, String> variables = new TreeMap<String, String>();

        variables.put("2",JavaExtensions.join(schemes, ", "));

        returnvariables;

    }

 

    @Override

    public booleanisSatisfied(Object validatedObject, Object value,

        OValContextcontext, Validator validator) throws OValException {

 

        requireMessageVariablesRecreation();

        try {

            finaljava.net.URI uri = new java.net.URI(value.toString());

            finalboolean schemeValid = schemes.contains(uri.getScheme());

            returnschemes.size() == 0 || schemeValid;

        } catch(URISyntaxException e) {

            returnfalse;

        }

    }

}

在渲染消息之前,The isSatisfied方法调用requireMessageVariablesRecreation()来指示OVal 去调用createMessageVariables()。返回一个整齐的变量map来传递消息格式。map keys 并不被使用; the "2" in this example indicates the message parameterindex. As before, the first parameter is the field name.

在模型里使用:

public class User {

   

    @URI(message ="validation.uri.schemes", schemes = {"http","https"})

    public Stringprofile;

}

消息定义如下:

validation.uri = Not a valid URI

validation.uri.schemes = &{%1$s} is not a valid URI -allowed schemes are %2$s

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值