05.用play验证http数据
验证确保了某些指定的需求能够获得正确的值。一般用于在存入数据库前对数据进行验证或表单验证。
在play里验证如何进行的?
每个请求都有他自己的Validation(验证)对象和相应的错误集合。有以下三种方式来定义验证。
- 在一个控制器方法,可以直接调用控制器的validation属性的方法。也可使用play.data.validation.Validation类的静态方法来访问API子集。
- 在控制器的方法参数上使用注释声明来进行验证。
- 为一个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
可通过以下三种方式定制验证消息:
- 重写应用程序的messages文件,对其中的消息进行重新定义。
- 提供一个定制消息作为附加的验证参数。
- 为局部消息提供一个消息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
本文介绍Play框架中HTTP数据验证的方法,包括控制器方法验证、使用注释声明验证、验证复杂对象及自定义验证等。同时提供了多种显示错误信息的方式。
1450

被折叠的 条评论
为什么被折叠?



