项目里面一定会有很多的action,每一个都需要验证参数的合法性,这是个很繁琐的工作,jfinal自带的拦截器功能用着还是觉得麻烦;参考hibernate-validator等现有的框架写了点东西,自我感觉美美的,特地来晒一晒
(内容挺长,不过关键点只有一丢丢)
废话不多说,上代码(我的环境jfinal-java8,版本3.3):
一、首先需要定义自己的注解(参照了现有的很多验证框架)
我这儿定义了挺多的,不过有个别的还没有完全实现,呵呵
贴出几个来打个样儿,其他的都类似
1. Length(被注解的字符串的大小必须在指定的范围内)
@Documented @Repeatable(LengthArray.class) @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.PARAMETER}) public @interface Length { String value(); int min(); int max(); String message() default "数据校验不合法"; }
2. NotBlank(被注解的字符串必须非null,且长度必须大于0),注意下Repeatable,这个是java1.8才有的
@Documented @Repeatable(NotBlankArray.class) @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.PARAMETER}) public @interface NotBlank { String value(); String message() default "数据必须有内容"; }
3. NotBlankArray(同2,这个表示同一位置可以出现多个相同的注解)
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.PARAMETER}) public @interface NotBlankArray { NotBlank[] value(); }
4.(被注释的元素必须符合指定的正则表达式)
@Documented //@Repeatable(LengthArray.class) @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.PARAMETER}) public @interface Pattern { String value(); String message() default "字段不符合规则"; String regexp(); }
说明:
value的值就是getPara("xxx");里面的xxx
message是校验失败时的提示信息
其他min,max,regexp等属性按需添加
二、然后编写action
public class TestController extends Controller { public void index(){ renderText("/test/index"); } @NotBlank(value="name", message="来者报上名来,空着算毛啊") @NotBlank(value="address") @Length(value="mobile", max = 11, min = 11, message="手机号码11位你不知道吗") //由数字、26个英文字母或下划线组成的字符串 @Pattern(value="content", regexp="^\\w+$") @URL(value="debug", host = "", port = 0, protocol = "") public void valid01(){ String name1 = getPara("name"); String address1 = getPara("address"); String mobile1 = getPara("mobile"); Integer age1 = getParaToInt("age"); String content1 = getPara("content"); String result = "[" + name1 + "/" + address1 + "/" + mobile1 + "/" + content1 + "/" + age1 + "]"; renderText("/test/valid01 " + result); } public void valid02( @NotBlank(value="")String name, @NotBlank(value="")String address, @Length(value="", max = 11, min = 11)String mobile, //由数字、26个英文字母或下划线组成的字符串 @Pattern(value="", regexp="^\\w+$")String content){ String name2 = getPara("name"); String address2 = getPara("address"); String mobile2 = getPara("mobile"); Integer age2 = getParaToInt("age"); String content2 = getPara("content"); String result = "[" + name2 + "/" + address2 + "/" + mobile2 + "/" + content2 + "/" + age2 + "]"; renderText("/test/valid02 " + result); } }
三、编写拦截器
代码看着挺长,不过都不复杂,前面的四个方法是关键、后面都是各个校验的具体代码,只看其中一两个就明白了,可以自由扩展哈,so easy~~~
public class ParameterValidateInterceptor implements Interceptor { @Override public void intercept(Invocation inv) { //处理method的注解 CheckResult checkResult = validateMethodAnnotation(inv); if (!checkResult.result) { inv.getController().renderJson( MyResult.build(7295, checkResult.paraName + ": " + checkResult.message) ); return; } //处理parameter的注解 checkResult = validateParameterAnnotation(inv); if (!checkResult.result) { inv.getController().renderJson( MyResult.build(7297, checkResult.paraName + ": " + checkResult.message) ); return; } //method和parameter注解验证都通过之后,才会继续调用action方法 inv.invoke(); } //处理方法上定义的注解 private CheckResult validateMethodAnnotation(Invocation inv){ CheckResult checkResult = new CheckResult(true, null, null); Annotation[] annotations = inv.getMethod().getDeclaredAnnotations();// .getAnnotations(); for (Annotation annotation : annotations) { checkResult = validateSingleAnnotation(annotation, inv, null, false); if (!checkResult.result) { return checkResult; } } return checkResult;//new CheckResult(true, null, null); } //处理参数上定义的注解 private CheckResult validateParameterAnnotation(Invocation inv){ CheckResult checkResult = new CheckResult(true, null, null); //遍历action方法的所有参数 Parameter[] parameters = inv.getMethod().getParameters(); for (Parameter parameter : parameters) { String paraName = parameter.getName(); //String paraValue = inv.getController().getPara(paraName); //遍历参数的所有注解 Annotation[] annotations = parameter.getDeclaredAnnotations(); for (Annotation annotation : annotations) { //System.out.println(" check parameter [ "+ paraName + "/" + paraValue + " ], annotition: " + annotation.annotationType().getSimpleName()); checkResult = validateSingleAnnotation(annotation, inv, paraName, true); if (!checkResult.result) { return checkResult; } } } return checkResult;//new CheckResult(true, null, null); } /** * 处理每个单独的注解 * @param annotation 发现的注解 * @param inv * @param paraName 参数名(方法的注解得从value去获取,现在还没有;参数的注解直接就是参数名) * @param type 类型:false说明是方法注解(还没有取到参数名paraName属性),true说明是参数注解(已经拿到了参数名paraName属性) * @return */ private CheckResult validateSingleAnnotation(Annotation annotation, Invocation inv, String paraName, boolean type){ CheckResult checkResult = new CheckResult(true, null, ""); if (annotation.annotationType().equals(NotBlank.class)) { NotBlank singleAnnotation = (NotBlank) annotation; checkResult = notBlankValidator(inv, singleAnnotation, type ? paraName : singleAnnotation.value()); } else if (annotation.annotationType().equals(NotBlankArray.class)) { NotBlankArray arrayAnnotation = (NotBlankArray) annotation; checkResult = notBlankArrayValidator(inv, arrayAnnotation); } else if (annotation.annotationType().equals(Length.class)) { Length singleAnnotation = (Length) annotation; checkResult = lengthValidator(inv, singleAnnotation, type ? paraName : singleAnnotation.value()); } else if (annotation.annotationType().equals(LengthArray.class)) { LengthArray arrayAnnotation = (LengthArray) annotation; checkResult = lengthArrayValidator(inv, arrayAnnotation); } else if (annotation.annotationType().equals(MobileNumber.class)) { MobileNumber singleAnnotation = (MobileNumber) annotation; checkResult = mobileNumberValidator(inv, singleAnnotation, type ? paraName : singleAnnotation.value()); } else if (annotation.annotationType().equals(NotNull.class)) { NotNull singleAnnotation = (NotNull) annotation; checkResult = notNullValidator(inv, singleAnnotation, type ? paraName : singleAnnotation.value()); } else if (annotation.annotationType().equals(Past.class)) { Past singleAnnotation = (Past) annotation; checkResult = pastValidator(inv, singleAnnotation, type ? paraName : singleAnnotation.value()); } else if (annotation.annotationType().equals(Future.class)) { Future singleAnnotation = (Future) annotation; checkResult = futureValidator(inv, singleAnnotation, type ? paraName : singleAnnotation.value()); } else if (annotation.annotationType().equals(Null.class)) { Null singleAnnotation = (Null) annotation; checkResult = nullValidator(inv, singleAnnotation, type ? paraName : singleAnnotation.value()); } else if (annotation.annotationType().equals(AssertTrue.class)) { AssertTrue singleAnnotation = (AssertTrue) annotation; checkResult = assertTrueValidator(inv, singleAnnotation, type ? paraName : singleAnnotation.value()); } else if (annotation.annotationType().equals(AssertFalse.class)) { AssertFalse singleAnnotation = (AssertFalse) annotation; checkResult = assertFalseValidator(inv, singleAnnotation, type ? paraName : singleAnnotation.value()); } else if (annotation.annotationType().equals(Email.class)) { Email singleAnnotation = (Email) annotation; checkResult = emailValidator(inv, singleAnnotation, type ? paraName : singleAnnotation.value()); } else if (annotation.annotationType().equals(URL.class)) { URL singleAnnotation = (URL) annotation; checkResult = urlValidator(inv, singleAnnotation, type ? paraName : singleAnnotation.value()); } else if (annotation.annotationType().equals(Pattern.class)) { Pattern singleAnnotation = (Pattern) annotation; checkResult = patternValidator(inv, singleAnnotation, type ? paraName : singleAnnotation.value()); } else { System.out.println("unknown method annotation ---------------- " + annotation.toString()); } return checkResult; } /** * @Null(value="xxx") * 校验:被注释的元素必须为 null */ private CheckResult nullValidator(Invocation inv, Null nullAnnotation, String paraName){ String paraValue = inv.getController().getPara(paraName); System.out.println(" ...... Null -- para: " + paraName + ", value: " + paraValue); //开始校验 return new CheckResult(paraValue==null, paraName, nullAnnotation.message()); } /** * @NotNull(value="xxx") * 校验:string参数xxx的值不能为 null */ private CheckResult notNullValidator(Invocation inv, NotNull notNullAnnotation, String paraName){ String paraValue = inv.getController().getPara(paraName); System.out.println(" ...... NotNull -- para: " + paraName + ", value: " + paraValue); //开始校验 return new CheckResult(paraValue!=null, paraName, notNullAnnotation.message()); } /** * @AssertTrue(value="xxx") * 校验: boolean参数xxx的值必须为true */ private CheckResult assertTrueValidator(Invocation inv, AssertTrue assertTrueAnnotation, String paraName){ Boolean paraValue = inv.getController().getParaToBoolean(paraName); System.out.println(" ...... AssertTrue -- para: " + paraName + ", value: " + paraValue); //开始校验 return new CheckResult(paraValue==null||paraValue, paraName, assertTrueAnnotation.message()); } /** * @AssertTrue(value="xxx") * 校验: boolean参数xxx的值必须为false */ private CheckResult assertFalseValidator(Invocation inv, AssertFalse assertFalseAnnotation, String paraName){ Boolean paraValue = inv.getController().getParaToBoolean(paraName); System.out.println(" ...... AssertFalse -- para: " + paraName + ", value: " + paraValue); //开始校验 return new CheckResult(paraValue==null||!paraValue, paraName, assertFalseAnnotation.message()); } /** * @Past(value="xxx") * 校验: date参数的xxx的值必须是已经过去的时间(jfinal目前只认“yyyy-MM-dd”的格式) */ private CheckResult pastValidator(Invocation inv, Past pastAnnotation, String paraName){ Date paraValue = inv.getController().getParaToDate(paraName); System.out.println(" ...... Past -- para: " + paraName + ", value: " + paraValue); //开始校验 return new CheckResult(paraValue==null||paraValue.getTime()<new Date().getTime(), paraName, pastAnnotation.message()); } /** * @Future(value="xxx") * 校验: date参数的xxx的值必须是将来的时间(jfinal目前只认“yyyy-MM-dd”的格式) */ private CheckResult futureValidator(Invocation inv, Future futureAnnotation, String paraName){ Date paraValue = inv.getController().getParaToDate(paraName); System.out.println(" ...... Future -- para: " + paraName + ", value: " + paraValue); //开始校验 return new CheckResult(paraValue==null||paraValue.getTime()>new Date().getTime(), paraName, futureAnnotation.message()); } /** * @Pattern(value="xxx", message="...", regexp="......") * 校验: string参数xxx的值必须能匹配正则表达式regexp(但可以为null或空串) */ private CheckResult patternValidator(Invocation inv, Pattern patternAnnotation, String paraName){ String paraValue = inv.getController().getPara(paraName); System.out.println(" ...... Pattern -- para: " + paraName + ", value: " + paraValue); //开始校验 if (StrKit.isBlank(paraValue)) { return new CheckResult(true, null, null); } java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(patternAnnotation.regexp()); Matcher matcher = pattern.matcher(paraValue); return new CheckResult(matcher.matches(), paraName, patternAnnotation.message()); } /** * @Email(value="xxx", message="...") * 校验: string参数xxx的值必须是合法的email地址,还未完成!!! */ private CheckResult emailValidator(Invocation inv, Email emailAnnotation, String paraName){ String paraValue = inv.getController().getPara(paraName); System.out.println(" ...... Pattern -- para: " + paraName + ", value: " + paraValue); //开始校验,参考【https://github.com/hibernate/hibernate-validator/blob/ff4324e6c182992c74110bfb51021a2bfee39d5e/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/EmailValidator.java】 if (StrKit.isBlank(paraValue)) { return new CheckResult(true, null, null); } String regexp = "\\w+@\\w+(\\.\\w+)+"; java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(regexp); Matcher matcher = pattern.matcher(paraValue); return new CheckResult(matcher.matches(), paraName, emailAnnotation.message()); } /** * @Length(value="xxx", min=mm, max=nn, message="...") * 校验: string参数xxx的值长度必须在min和max的范围内 */ private CheckResult lengthValidator(Invocation inv, Length lengthAnnotation, String paraName){ String paraValue = inv.getController().getPara(paraName); System.out.println(" ...... Length -- para: " + paraName + ", value: " + paraValue); //开始校验 int min = lengthAnnotation.min(); int max = lengthAnnotation.max(); return new CheckResult(paraValue!=null&&(paraValue.length()>=min&¶Value.length()<=max), paraName, lengthAnnotation.message()); } /** * @Length(value="xxx",...) * @Length(value="yyy",...) * 同一位置有多个@Length注解 */ private CheckResult lengthArrayValidator(Invocation inv, LengthArray lengthArrayAnnotation){ for (Length lengthAnnotation : lengthArrayAnnotation.value()) { CheckResult checkResult = lengthValidator(inv, lengthAnnotation, lengthAnnotation.value()); if(!checkResult.result){ return checkResult; } } return new CheckResult(true, null, null); } /** * @NotBlank(value="xxx", message="...") * 校验: string参数xxx的值不能为null,且长度必须大于0 */ private CheckResult notBlankValidator(Invocation inv, NotBlank notBlankAnnotation, String paraName){ String paraValue = inv.getController().getPara(paraName); System.out.println(" ...... NotBlank -- para: " + paraName + ", value: " + paraValue); //开始校验 return new CheckResult(paraValue!=null && paraValue.trim().length() > 0, paraName, notBlankAnnotation.message()); } /** * @NotBlank(value="xxx") * @NotBlank(value="yyy") * 同一位置有多个@NotBlank注解 */ private CheckResult notBlankArrayValidator(Invocation inv, NotBlankArray notBlankArrayAnnotation){ for (NotBlank notBlankAnnotation : notBlankArrayAnnotation.value()) { CheckResult checkResult = notBlankValidator(inv, notBlankAnnotation, notBlankAnnotation.value()); if(!checkResult.result){ return checkResult; } } return new CheckResult(true, null, null); } /** * @URL(value="xxx", protocol="aaa", host="bbb", port=zzz, message="") * 校验:string参数xxx必须是一个有效的url,还未完成 */ private CheckResult urlValidator(Invocation inv, URL urlAnnotation, String paraName){ String paraValue = inv.getController().getPara(paraName); System.out.println(" ...... URL -- para: " + paraName + ", value: " + paraValue); //开始校验 if (StrKit.isBlank(paraValue)) { return new CheckResult(true, paraName, null); } String regexp = "^(?:https?://)?[\\w]{1,}(?:\\.?[\\w]{1,})+[\\w-_/?&=#%:]*$"; java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(regexp); Matcher matcher = pattern.matcher(paraValue); return new CheckResult(matcher.matches(), paraName, urlAnnotation.message()); //下面这个方法复杂些,可以验证协议、主机、端口,等等 // CheckResult checkResultSuccess = new CheckResult(true, paraName, null); // CheckResult checkResultFail = new CheckResult(false, paraName, urlAnnotation.message()); // if ( paraValue == null || paraValue.length() == 0 ) { // return checkResultSuccess; // } // //^(?:https?://)?[\\w]{1,}(?:\\.?[\\w]{1,})+[\\w-_/?&=#%:]*$ // java.net.URL url; // try { // url = new java.net.URL( paraValue ); // } // catch (MalformedURLException e) { // return checkResultFail; // } // if ( urlAnnotation.protocol() != null && urlAnnotation.protocol().length() > 0 && !url.getProtocol().equals( urlAnnotation.protocol() ) ) { // return checkResultFail; // } // if ( urlAnnotation.host() != null && urlAnnotation.host().length() > 0 && !url.getHost().equals( urlAnnotation.host() ) ) { // return checkResultFail; // } // if ( urlAnnotation.port() != -1 && url.getPort() != urlAnnotation.port() ) { // return checkResultFail; // } // return checkResultSuccess; } /** * @MobileNumber(value="xxx") * 验证: string参数xxx的值必须是合法的手机号码 */ private CheckResult mobileNumberValidator(Invocation inv, MobileNumber mobileNumberAnnotation, String paraName){ String paraValue = inv.getController().getPara(paraName); System.out.println(" ...... MobileNumber -- para: " + paraName + ", value: " + paraValue); //开始校验 if (StrKit.isBlank(paraValue)) { return new CheckResult(true, paraName, null); } String regexp = "^((17[0-9])|(14[0-9])|(13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$"; java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(regexp); Matcher matcher = pattern.matcher(paraValue); return new CheckResult(matcher.matches(), paraName, mobileNumberAnnotation.message()); } class CheckResult { private boolean result; private String paraName; private String message; public CheckResult(boolean result, String paraName, String message) { super(); this.result = result; this.paraName = paraName; this.message = message; } } }
还有个MyResult,也贴出来吧
public class MyResult { private Integer status; private String message; private Integer totalPage; private Integer totalNum; private Object data; private static final Integer SUCCESS_CODE = 0; private static final String SUCCESS_MSG = "success"; // get,set 略 @Override public String toString() { return "MyCustomResult [status=" + status + ", message=" + message + ", totalPage=" + totalPage + ", totalNum=" + totalNum + ", data=" + data + "]"; } // 构造方法 public MyResult(){ } public MyResult(Integer status, String message, Object data) { this.status = status; this.message = message; if (data instanceof Page) { this.data = ((Page<?>) data).getList(); setTotalNum(((Page<?>) data).getTotalRow()); setTotalPage(((Page<?>) data).getTotalPage()); } else { this.data = data; } } // 静态方法 public static MyResult build(Integer status, String message, Object data){ return new MyResult(status, message, data); } public static MyResult build(Integer status, String message){ return new MyResult(status, message, null); } public static MyResult ok(Object data){ return new MyResult(SUCCESS_CODE, SUCCESS_MSG, data); } public static MyResult ok(){ return new MyResult(SUCCESS_CODE, SUCCESS_MSG, null); } }
四、配置路由和拦截器
@Override public void configRoute(Routes me) { me.add("test", TestController.class); }
@Override public void configInterceptor(Interceptors me) { me.addGlobalActionInterceptor(new ParameterValidateInterceptor()); }
五、测试喽~~~
我用的是Postman,贴图
六、谢谢~~~