让校验工作更加轻松

用户输入校验是无法避免的工作。一个页面输入,我们要校验一些内容不能为空,一些内容长度必须满足条件,一些内容必须是整型---虽然没啥技术含量,但是还得做,而且还得做仔细了。

那么能不能让这部分的工作量减少一点?光说不练怎么行?下面就做做看!
看看下面这个类:

public class TestForm {

private String userName;
private int userAge;
private String userBirth;

省略 get/set 方法
}


这是一个普通的form,页面提交的内容会自动填充到这里----地球人都知道。

那么,针对这个form的校验,怎么才能简单点呢?最初的想法是这样:

public class TestForm {
@NotNull
public String getUserBirth() {
return userBirth;
}


这样,如果有 @NotNull 标注的,就会检查是不是空。不过这样不好。因为变量在不同的页面里,检查的条件有可能不一样。所以,就弄出了这么一个接口:

public interface IValidateTestForm {

@NotNull
public String userName();

@MoreThanZero
public int userAge();

@NotNull
public String userBirth();
}


这个接口里的方法应该与 form 的方法一致,这样就可以对应着去校验。
好,下面我们首先来编写测试方法,看看我的目的是什么:

public void testValidate() {

TestForm form = new TestForm();

List<ValidateEntry> list = AutoValidator.validate(form, IValidateTestForm.class);

assertEquals(3, list.size());

assertTrue(errorExist("userAge", "必须是大于等于0的有效数字。", list));
assertTrue(errorExist("userBirth", "不能为空。", list));
assertTrue(errorExist("userName", "不能为空。", list));

}

private boolean errorExist(String entryName, String entryValue, List<ValidateEntry> list) {

for(ValidateEntry entry : list) {

if(entryName.equals(entry.getEntryName()) &&
entryValue.equals(entry.getEntryValue())) {

return true;
}
}
return false;
}


嗯,我先假设使用了一个叫 AutoValidator 的类来做这个工作,然后还假设了一个 ValidateEntry 类来保存错误信息。假设是没用的,现在来创建这两个类。

public class ValidateEntry {

private String entryName;
private String entryValue;
public String getEntryName() {
return entryName;
}
public void setEntryName(String entryName) {
this.entryName = entryName;
}
public String getEntryValue() {
return entryValue;
}
public void setEntryValue(String entryValue) {
this.entryValue = entryValue;
}

}

这个ValidateEntry就是保存了一个错误信息,包括错误的项目的名字以及错误的信息的内容。

AutoValidator 类有点长,贴出来影响阅读,可以在附件中下载。这里就说说思路,通过传入的 IValidateTestForm, 根据里面的方法,去得到 form 里对应的 get 方法,然后检查方法所具有的标注,再调用标注里面的变量,进行校验。例如这个 NotNull 标注应该是这个样子:


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NotNull {

public IItemValidator validator = new NotNullValidator();
public String itemName() default "";
public String errorMsg() default "";
}


这里又出来了 IItemValidator 接口:


public interface IItemValidator {

public boolean validate(Object obj, Object... params);
public String getErrorMessage();
}


这个接口有2个方法:一个是进行校验(咦,最后为啥带个不确定的参数类型?后面会分析到);还有一个方法是返回错误的信息内容。
而具体的、各种各样的 Validator,都必须要实现这个接口。

例如这个 NotNullValidator 就是下面这个样子:

public class NotNullValidator implements IItemValidator{

public boolean validate(Object obj, Object... params) {

if(obj == null) {
return false;
}

if(obj instanceof String) {
if(BtmsUtil.isBlank((String)obj)) {
return false;
}
} else if(obj instanceof String[]) {
if(BtmsUtil.isBlank((String[]) obj)) {
return false;
}
}

return true;
}

public String getErrorMessage() {

return "不能为空。";
}
}

再回头看看那个 NotNull 注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NotNull {

public IItemValidator validator = new NotNullValidator();
public String itemName() default "";
public String errorMsg() default "";
}

注解中必须定一个 IItemValidator 的变量,而且名字必须是 validator,在 AutoValidator 类中,正是通过 "validator" 这个名字来找到这个变量的。

itemName 是错误项目的名称,如果为空,那么就使用变量的名字。这个方法的名字也必须是“itemName”。

errorMsg 是错误消息的内容。如果为空,那么就使用 validator里的错误消息。这个方法的名字也必须是“errorMsg”。

恩,这个注解必须要遵从一些约定。
其实,我也想过在注解中再添加注解,例如

public @interface NotNull {

public IItemValidator validator = new NotNullValidator();
@ItemName
public String itemName() default "";
public String errorMsg() default "";
}

可是jdk在运行时死活就是发现不了注解中的注解,很是无奈呀!

好,下面看看我们究竟是如何做到减少校验的工作量的。
1、我们需要准备一些 validator,例如 NotNullValidator,这些类必须实现 IItemValidator 接口。
2、我们需要准备一些annotation,例如 NotNull, MoreThanZero 等等。这些 annotation 必须遵守一定的规则。
------- 以上这些活应该是一个负责搭建环境或者架构的人来做的 ---------------
------- 而且也希望以上这些东西可以重复用在不同的项目中 -------------------

3、针对一个 form 的校验,我们需要创建一个接口,类似于上面的 IValidateTestForm。需要校验的字段增加相应的注解。
4、最后,我们进行如下这样的调用:

List<ValidateEntry> list = AutoValidator.validate(form, IValidateTestForm.class);

开发者可以得到list 中的错误项目,自己组织错误信息的显示方式。
-------- 以上这些活就是一个开发者要做的 -------------------------------

开发者的工作量是不是减轻了不少?不过目前这个校验还不完善,有的校验我们需要传递给 validator 参数,例如 MaxLength 这样的校验,validator 需要知道一个 max length 是多少。所以,我们再次改造注解:

public @interface MaxLength {

public String itemName() default "";
public IItemValidator validator = new MaxLengthValidator();
public int[] params();

public String errorMsg() default "";
}

我们增加了一个方法叫 params。并且我们约定这个方法必须叫 "params"。类型任意,只要 annotation 允许就可了。然后我们再修改一下校验用的接口:

@NotNull(itemName="用户名称")
@MaxLength(itemName="用户名称", params = {20})
public String userName();

还记得我们的 validator 中,校验方法中有一个不定长的参数吧?嗯,就是为了传递这个参数。

好了,说了这么多,不知道大伙是否能感觉到减轻了校验的工作量呢?我想还是看代码最实际,附件中有完整的代码,可以运行测试类来看看效果。

如果您对这样的小东西感兴趣,欢迎给提出改进的意见,我可以继续完善,因为我实在是受够了做校验这样的无聊的活 :wink:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值