play框架使用起来(14)-高级指南

高级指南
1、HTTP数据验证

 数据验证是应用程序健壮性的体现,在实际项目中也是必不可少的环节。Play内置了验证器(Validation)的支持,并提供了非常灵活的使用方法。在Play项目中可以很简单地对数据,模型对象(可能需要持久化)以及HTTP表单进行验证。


1.1 使用Play验证器#

      出于数据验证的考虑,框架为每个请求绑定了验证器。应用代码中可以通过以下三种方式对数据进行验证:

      1.在控制器的Action方法中,直接使用验证器对数据进行相应的验证:

public static void hello(String name) {
     validation
.required(name);  //验证name参数是否被赋值
     
...
}


      2.在控制器的Action方法的参数中,使用注解进行数据验证:

public static void hello(@Required String name){
   
...
}


      3.定义域模型时直接为属性添加验证注解,Action中就可以通过@Valid注解对该POJO参数进行验证了:

public class User extends Model {
   
@Required
   
public String name;  //为User的name属性添加@Required验证
   
...
}
//直接使用@Valid注解对POJO参数进行验证
public static void save(@Valid User user) {
   
...
}


注意:

框架为每个请求绑定了一个验证器,同个验证器可以校验多个数据。


      当验证不通过时,验证器会将错误以play.data.validation.Error的形式保存,因此每个验证器以集合的形式维护了一系列的error对象。每个error对象有key和message两个属性:

  • key:该属性帮助我们标识引起错误的元素。key的值可以任意设定,默认与验证的数据同名。
  • message:验证消息,用于描述验证不通过的错误信息。message可以是纯文本或指向消息包(message bundle)的key(通常为了支持国际化)。


      文字的描述可能有些空洞,接下来让我们看看如何使用验证器校验简单的HTTP参数:

public static void hello(String name) {
     validation
.required(name);
     
...
}

      以上这段程序代码检查了name参数是否被赋值。如果没有,相应的错误就会被添加到当前的error集合中。如果有多个数据需要验证,可以重复该操作:

public static void hello(String name, Integer age) {
     validation
.required(name);     //验证name是否被赋值
     validation
.required(age);      //验证age是否被赋值
     validation
.min(age, 0);        //验证age不小于0
     
...
}

1.2 验证消息(error对象中的message属性)#

      在验证结束后我们可以检查是否有error产生,并将验证消息打印出来:

public static void hello(String name, Integer age) {
     validation
.required(name);
     validation
.required(age);
     validation
.min(age, 0);
     
     
if(validation.hasErrors()) {                
         
for(Error error : validation.errors()) {
             
System.out.println(error.message());
         
}
     
}
}

      假定name和age都没有赋值,控制台将会打印以下信息:

Required
Required

      这是因为在$PLAY_HOME/resources/messages文件中定义了默认的消息:

validation.required=Required

      Play提供了三种方式自定义验证消息:

  1. 在项目的conf/messages文件中自定义消息,覆盖默认的message消息。
  2. 直接将自定义的验证消息作为参数传递给验证器,覆盖默认的message消息。
  3. 定义带参数的验证消息,参数通常为error对象的key值。


本地化的验证消息

      我们可以配置应用程序的conf/messages文件,采用统一的验证消息匹配error对象的message消息,这是最简单的覆盖消息的写法:

validation.required = Please enter a value 
      经过以上配置后,任何不通过validation.required验证的消息都是Please enter a value。


带参数的验证消息

      %s占位符将被替换成error对象的key:

validation.required=%s is required

      结合以上例子,输出结果为:

name is required
age
is required

      框架默认将被验证的参数的名字作为error对象的key值进行传递,但也有其他方法修改error对象的key值。比如,针对上例中hello方法(Action)的name参数进行本地化处理:

name = Customer name

      结果就会变成:

Customer name is required
age
is required

      也可以使用error.message(String key)方法直接覆盖error对象的key值:

Error error = validation.required(name).error;
if(error != null) {
   
System.out.println(error.message("Customer name"));
}

      Play内置了很多验证方法,也提供了不同的消息参数。比如match验证,在消息表达式中定义了字符串类型的参数,与前面介绍的%s的占位符不同,其规定的参数索引为2:

validation.match=Must match %2$s

      类似的,range验证定义了两个整数型参数,以2和3作为索引:

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

      读者可以在$PLAY_HOME/resources/messages文件中查看其他的验证以及所含的参数。


补充:

读者可能会有疑问,为什么这些参数的索引都是从2开始的。因为match,range,minSize等验证都是需要比较的,框架规定将索引为1的参数设置为比较源。


自定义本地化验证消息

      Play内置验证器的message消息,是在$PLAY_HOME/resources/messages文件中定义的。我们也可以在项目中定义自己的验证消息:

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

      在Action方法中,就可以采用自己定义的验证消息机制,手动进行验证:

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

      我们也可以把自己定义的验证消息机制作为注解中message的参数使用:

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

      同理,JavaBean的属性中也可以使用这种验证技术(注解在JavaBean的属性前):

public class Person extends Model { 
   
@Required(message = "validation.required.em")
   
public String name;
   
...
}
public static void hello(@Valid Person person) { 
   
...
}


更灵活的自定义方式

      Play还提供了一种非常灵活的方式实现验证消息的自定义,直接在代码中为验证器标注message消息:

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

      在注解中的使用方法:

public static void save(@Required(message = "Give us a name!") String name) { 
   
...
}

      在JavaBean中的使用方法:

public class Person extends Model {
   
@Required(message = "Give us a name!")
   
public String name;
   
...
}
public static void save(@Valid Person person) { 
   
...
}

1.3 模板中显示错误信息#

      如果需要将验证的错误信息在视图模板中显示,Play提供的#{ifErrors}标签和error对象可以轻松完成这些工作。在hello Action中进行数据验证,并使用默认的hello.html模板显示:

public static void hello(String name, Integer age) {
     validation
.required(name);
     validation
.required(age);
     validation
.min(age, 0);
     render
(name, age);
}

      编辑view/Application/hello.html模板内容,使用#{ifErrors}标签显示验证结果信息:

#{ifErrors}
 
   
<h1>Oops...</h1>
 
   
#{errors}
       
<li>${error}</li>
   
#{/errors}
 
#{/ifErrors}
#{else}
 
   
Hello ${name}, you are ${age}.
 
#{/else}

      显然,以上的代码并不能满足真实应用的需求。因为在实际项目中,如果验证失败了就应该显示原来的表单,并提示用户请再次输入。针对这个需求,我们需要定义两个Action:一个用于显示表单,另一个用于处理POST,数据的验证将发生在后一个操作。当验证有错误发生时,保存错误信息并重定向到第一个Action。Play提供的validation.keep()方法可以在下一个请求中保持错误信息的集合。以下是真实项目示例:

public class Application extends Controller {
 
   
public static void index() {
      render
();
   
}
 
   
public static void 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>
     
<input type="submit" value="Say hello" />
   
</div>
#{/form}

      为了提供更好的用户体验,我们可以将错误信息显示在未通过验证的字段旁:

#{ifErrors}
   
<h1>Oops…</h1>
#{/ifErrors}
 
#{form @Application.hello()}
   
<div>
     
Name: <input type="text" name="name" value="${flash.name}" />
     
<span class="error">#{error 'name' /}</span>
   
</div>
   
<div>
     
Age: <input type="text" name="age" value="${flash.age}" />
     
<span class="error">#{error 'age' /}</span>
   
</div>
   
<div>
     
<input type="submit" value="Say hello" />
   
</div>
#{/form}


1.4 注解方式#

      注解封装于play.data.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);
}
      相比之下,使用注解的代码就显得整洁的多了。


1.5 模型对象的验证#

      在Play中通常采用注解的形式为模型对象添加数据验证。重写前面所介绍的例子,为User实体的name属性添加@Required验证注解,age属性添加@Required和@Min(0)验证注解:

package models;
 
public class User {
   
   
@Required
   
public String name;
 
   
@Required
   
@Min(0)
   
public Integer age;
}

      接着修改hello Action方法,使用@Valid注解对模型对象进行数据验证:

public static void hello(@Valid User user) {
   
if(validation.hasErrors()) {
       
params.flash();    // 将HTTP参数保存在flash作用域中
       validation
.keep(); // 在下一个request中保持错误信息的集合
       index
();
   
}
   render
(user);
}

      最后修改index.html表单,如果验证有错,在页面上显示提示信息:

#{ifErrors}
   
<h1>Oops...</h1>
#{/ifErrors}
 
#{form @Application.hello()}
   
<div>
     
Name: <input type="text" name="user.name" value="${flash['user.name']}" />
     
<span class="error">#{error 'user.name' /}</span>
   
</div>
   
<div>
     
Age: <input type="text" name="user.age" value="${flash['user.age']}" />
     
<span class="error">#{error 'user.age' /}</span>
   
</div>
   
<div>
     
<input type="submit" value="Say hello" />
   
</div>
#{/form}

1.6 内置验证#

      在框架的play.data.validation包中,系统已经定义了若干内置的验证机制,这些常用的验证如表1所示:

(表1 内置验证)

名称 验证内容 名称 验证内容
email 验证E-mail地址是否合法 equals 验证两个值是否相等
future 验证是否为相对未来的时间 isTrue 验证String或者Boolean类型变量是否为true
max 验证数值大小是否大于给定的值 maxSize 验证字符串长度是否大于给定的值
min 验证数值大小是否小于给定的值 minSize 验证字符串长度是否小于给定的值
match 验证是否匹配给定的正则表达式 past 与future相对,验证是否为相对过去的时间
range 验证是否在给定的两个数值范围内 required 验证是否为空
url 验证是否为合法的URL phone 验证是否为合法的电话号码
ipv4Address 验证是否为符合ipv4规则的IP地址 ipv6Address 验证是否为符合ipv6规则的IP地址

   其中,required、min以及range验证器在前几节介绍的示例中已经有所涉及。本小节将着重介绍其中的email、match与phone验证器。除此之外的几类验证器都非常简单实用,就不再做过多的叙述了。

email

      使用email验证器可以对给定的E-mail地址是否正确进行验证,其message key为validation.email。下面通过示例演示如何使用email验证器对给定的E-mail地址进行检测。

validation.email(address);

      对变量address进行email验证,如果address不是有效的E-mail地址,验证器会将错误以play.data.validation.Error的形式保存。email验证同样也支持注解用法,读者在定义model时可以为模型的属性添加@Email注解:

@Email String address

match

      match验证器是相对比较特别的验证器,该验证器可以使用正则表达式(用于描述或者匹配一系列符合某个句法规则的字符串的单个字符串)作为校验规则。在使用match验证器时,需要制定充当验证规则的正则表达式字符串作为参数,注意空字符串将会被认为是合法的,其message key为validation.match。下面通过示例演示如何使用match验证器对给定的字符串进行检测。

validation.match(abbreviation, "[A-Z]{3}"); // TLA

      对变量abbreviation进行match验证,如果abbreviation不能匹配正则表达式,验证器会将错误以play.data.validation.Error的形式保存。match验证同样也支持注解用法,读者在定义model时可以为模型的属性添加@match注解:

@Match("[A-Z]{3}") String abbreviation


phone

      phone验证器可以对电话号码进行校验,验证是否合法。在使用phone验证器时需要注意,空字符串也将被认为是合法的,其message key为 validation.phone。下面通过示例演示如何使用phone验证器对给定的电话号码进行检测。

validation.phone(value);

      对变量value进行phone验证,如果value不是合法的电话号码,验证器会将错误以play.data.validation.Error的形式保存。phone验证同样也支持注解用法,读者在定义model时可以为模型的属性添加@phone注解:

@Phone String phone
      读者在使用时需要注意,phone验证器的底层实现是基于电话号码模式匹配的,其准确性并不是特别高。如果希望实现准确的电话号码验证器,推荐使用@match验证器制定不同国家的电话号码验证方式。电话号码的自定义格式可以是+CCC (SSSSSS)9999999999xEEEE,其中:
  • +是可选的,代表国家码。
  • CCC是可选的,代表前3位国家代码,需要注意的是其后必须要有一个分隔符。
  • (SSSSSS)是可选的,代表6位地区码。
  • 9999999999是必须,表示电话号码,最高为20位(已覆盖了所知的情况和未来可能的号码)。
  • x是可选的,表示扩展,也可以写成“ext”或“extension”。
  • EEEE是可选的,代表扩展码,最多4位。
  • 分隔符可以使用空格、‘-’、‘.’或是‘/’其中的一个,并且可以在号码的任意地方使用。

      下面是不同国家电话号码的样例,提供给读者们参考:

  • 中国:+86 (10)69445464
  • 美国:(305) 613 09 58 ext 101
  • 法国:+33 1 47 37 62 24 x3
  • 德国:+49-4312 / 777 777
  • 英国:(020) 1234 1234

1.7 自定义验证#

      自定义的验证器可以通过@CheckWith注解进行绑定:

public class User {
   
   
@Required
   
@CheckWith(MyPasswordCheck.class)
   
public String password;
   
   
static class MyPasswordCheck extends Check {
       
       
public boolean isSatisfied(Object user, Object password) {
           
return notMatchPreviousPasswords(password);
       
}
   
}
}

注意:

自定义验证器时不要忘记加载play.data.validation包。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值