您的位置:首页 > 其它

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

2016-11-04 15:27 399 查看
高级指南
[b]1、HTTP数据验证
[/b]

 数据验证是应用程序健壮性的体现,在实际项目中也是必不可少的环节。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提供了三种方式自定义验证消息:
在项目的conf/messages文件中自定义消息,覆盖默认的message消息。
直接将自定义的验证消息作为参数传递给验证器,覆盖默认的message消息。
定义带参数的验证消息,参数通常为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验证是否为合法的URLphone验证是否为合法的电话号码
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包。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: