spring mvc前端验证代码生成器

时间:2021-11-04 21:59:42

spring mvc只提供了服务端验证功能,客户端验证需要自己编写,这是一项重复枯燥的工作,所以考虑基于验证注解自动生成客户端验证代码

一、自定义EL函数

 

/**
* 自定义EL函数调用
*
* @author ouyang
*
*/
public class OkweiEL {

public static final String REDIS_PACKAGE = "wei.wap.form";

private static final String HTML = "<script type='text/javascript'> \r\n"
+ "$().ready(function() { \r\n"
+ " if(!$.isFunction($.fn.validate)) { alert('未加载validation'); return; }\r\n"
+ "$('%s').validate({ \r\n" + "rules : { %s \r\n"
+ "},\r\n" + "messages : { %s \r\n" + " }, \r\n" + " %s "
+ "}) \r\n;" + "});" + "//from %s</script>";

/**
* 默认的错误显示位置处理
*/
private static final String ErrorPlacementHandlerDefault = " errorPlacement: function(error, element) { $('<br/>').appendTo(element.parent()); error.appendTo(element.parent()); } ";

/**
* 自定义的错误显示位置处理
*/
private static final String ErrorPlacementHandlerCustom = " errorPlacement: function(error, element) { %s(error,element); } ";

private static final String ERROR_CLASS = "";



private static final String ERROR_HTML = "<script type='text/javascript'>alert('%s');</script>";

public static String validate(String className, String pathList,
String formSelector, String redisVersion) {
return validate(className, pathList, formSelector, redisVersion, null);
}

/**
* 前端验证生成器,作为自定义EL函数调用 依赖 1.引入jquery validation.js 2.定义form bean
* 带验证注解,可包含自定义验证注解 放在jsp页面引入所有js的代码后面 规则参考 已做缓存,更新策略:className对应的class被修改
* http://www.runoob.com/jquery/jquery-plugin-validate.html
*
* @param className
* 类名,可定义一个页面全局变量存储再传入本函数,以免重复写多次该类名
* @param pathList
* 属性列表,意义同 spring mvc form tag里面的path,多个用,分隔,为空默认处理所有属性
* @param formSelector
* form 选择器
* @param redisVersion
* redis版本,可为空;rediskey = form class修改日期+redis版本 的组合
* @param errLocationJsHandler
* 处理错误信息显示位置的 js 函数,如果为空则按默认位置处理
* @return html
*/
public static String validate(String className, String pathList,
String formSelector, String redisVersion,
String errLocationJsHandler) {

// 根据class文件修改时间做缓存
String classPath = OkweiEL.class.getResource("/").getPath();
classPath = classPath.replaceAll("%20", " ")
+ className.replace(".", "/") + ".class";
long modified = LoaderTagSupport.getLastModified(classPath);
String redisKey = REDIS_PACKAGE
+ "."
+ className.substring(className.indexOf("form.") + 5)
+ "."
+ modified
+ (StringHelp.IsNullOrEmpty(redisVersion) ? "" : "."
+ redisVersion);
String validateJs = RedisUtil.getString(redisKey);
if (!StringHelp.IsNullOrEmpty(validateJs)) {
return validateJs.replace("from ori", "from re" + modified);
}

StringBuilder rules = new StringBuilder();
StringBuilder messages = new StringBuilder();
try {

// 动态产生 validation js
Class<?> act = Class.forName(className);
Field[] fieldList = null;
if (StringHelp.IsNullOrEmpty(pathList))
fieldList = act.getDeclaredFields();
else {
String[] pathArr = pathList.split(",");
fieldList = new Field[pathArr.length];
for (int i = 0; i < pathArr.length; i++) {
fieldList[i] = act.getDeclaredField(pathArr[i]);
}
}

for (Field field : fieldList) {

String name = field.getName();
rules.append(name + ":{");
messages.append(name + ":{");

Method curMethod = act.getMethod("get"
+ name.substring(0, 1).toUpperCase()
+ name.substring(1));

NotNull notNull = curMethod
.getDeclaredAnnotation(NotNull.class);
NotBlank notblank = curMethod
.getDeclaredAnnotation(NotBlank.class);
NotEmpty notempty = curMethod
.getDeclaredAnnotation(NotEmpty.class);
// 最小整数
Min min = curMethod.getDeclaredAnnotation(Min.class);
// 最大整数
Max max = curMethod.getDeclaredAnnotation(Max.class);
// 任何正则匹配
Pattern pattern = curMethod
.getDeclaredAnnotation(Pattern.class);
// 2个属性对比
EqualTo equalTo = curMethod
.getDeclaredAnnotation(EqualTo.class);
BiggerThan bt = curMethod
.getDeclaredAnnotation(BiggerThan.class);
// 远程校验
Remote remote = curMethod.getDeclaredAnnotation(Remote.class);

// 任意数字,可限定小数个数,非小数个数
Digits digits = curMethod.getDeclaredAnnotation(Digits.class);
// 最小小数
DecimalMin decimalMin = curMethod
.getDeclaredAnnotation(DecimalMin.class);
// 最大小数
DecimalMax decimalMax = curMethod
.getDeclaredAnnotation(DecimalMax.class);
// 字符长度范围
Size size = curMethod.getDeclaredAnnotation(Size.class);
// 字符长度范围
Length len = curMethod.getDeclaredAnnotation(Length.class);
// 任意数字范围,也可用于简单的验证是否数字 @Range(message="请输入数字")
Range range = curMethod.getDeclaredAnnotation(Range.class);


if (digits != null) {
rules.append("pattern:['"
+ String.format(ValidatorConstant.DIGITS,
digits.integer(), digits.fraction())
.replace("\\", "\\\\") + "',");
if (!StringHelp.IsNullOrEmpty(digits.message()))
rules.append("'" + digits.message() + "'],");
else {
rules.append("'],");
}
}

if (range != null) {
rules.append("range:[" + range.min() + "," + range.max()
+ "],");
if (!StringHelp.IsNullOrEmpty(range.message()))
messages.append("range:'" + range.message() + "',");
}
if (size != null) {
rules.append((size.min() > 0 ? "required:true," : "")
+ "rangelength:[" + size.min() + "," + size.max()
+ "],");
if (!StringHelp.IsNullOrEmpty(size.message()))
messages.append((size.min() > 0 ? "required:'"
+ size.message() + "'," : "")
+ "rangelength:'" + size.message() + "',");
}
if (len != null) {
rules.append((len.min() > 0 ? "required:true," : "")
+ "rangelength:[" + len.min() + "," + len.max()
+ "],");
if (!StringHelp.IsNullOrEmpty(len.message()))
messages.append((len.min() > 0 ? "required:'"
+ len.message() + "'," : "")
+ "rangelength:'" + len.message() + "',");
}
if (notNull != null) {
rules.append("required:true,");
if (!StringHelp.IsNullOrEmpty(notNull.message()))
messages.append("required:'" + notNull.message() + "',");
}
if (notblank != null) {
rules.append("required:true,");
if (!StringHelp.IsNullOrEmpty(notblank.message()))
messages.append("required:'" + notblank.message()
+ "',");
}
if (notempty != null) {
rules.append("required:true,");
if (!StringHelp.IsNullOrEmpty(notempty.message()))
messages.append("required:'" + notempty.message()
+ "',");
}
if (min != null) {
rules.append("min:" + min.value() + ",");
if (!StringHelp.IsNullOrEmpty(min.message()))
messages.append("min:'" + min.message() + "',");
}
if (decimalMin != null) {
rules.append("min:" + decimalMin.value() + ",");
if (!StringHelp.IsNullOrEmpty(decimalMin.message())) {

messages.append("min:'" + decimalMin.message() + "',");
}
}
if (decimalMax != null) {
rules.append("max:" + decimalMax.value() + ",");
if (!StringHelp.IsNullOrEmpty(decimalMax.message())) {
messages.append("max:'" + decimalMax.message() + "',");
}
}
if (max != null) {
rules.append("max:" + max.value() + ",");
if (!StringHelp.IsNullOrEmpty(max.message()))
messages.append("max:'" + max.message() + "',");
}
if (equalTo != null) {
rules.append("equalTo:'#" + equalTo.toProperty() + "',");
if (!StringHelp.IsNullOrEmpty(equalTo.message()))
messages.append("equalTo:'" + equalTo.message() + "',");
}

if (remote != null) {
String[] pathList2 = remote.pathList().split(",");
StringBuilder ajaxParams = new StringBuilder();
for (String string : pathList2) {
ajaxParams.append(string + ": function() { return $('#"
+ string + "').val();},");
}
ajaxParams.deleteCharAt(ajaxParams.length() - 1);
rules.append("remote: {" + "url: \"" + remote.url()
+ "\", \r\n" + "type: \"post\", \r\n"
+ "dataType: \"json\", \r\n" + "data: {"
+ ajaxParams.toString() + "} \r\n " + "} \r\n "
+ ",");
if (!StringHelp.IsNullOrEmpty(remote.message()))
messages.append("remote:'" + remote.message() + "',");
}

if (pattern != null) {
rules.append("pattern:['"
+ pattern.regexp().replace("\\", "\\\\") + "',");
if (!StringHelp.IsNullOrEmpty(pattern.message()))
rules.append("'" + pattern.message() + "'],");
else {
rules.append("'],");
}
}
if (bt != null) {
rules.append("biggerthan:['" + bt.toProperty() + "',");
if (!StringHelp.IsNullOrEmpty(bt.message()))
rules.append("'" + bt.message() + "'],");
else {
rules.append("'],");
}
}

rules.append("},");
messages.append("},");
}

} catch (ClassNotFoundException e) {
return String.format(ERROR_HTML, "OkweiEL.validate:类" + className
+ "不存在");
} catch (NoSuchMethodException e) {
return String.format(ERROR_HTML,
"OkweiEL.validate,方法不存在:" + e.getMessage());
} catch (NoSuchFieldException | SecurityException e) {
return String.format(ERROR_HTML,
"OkweiEL.validate,属性不存在:" + e.getMessage());
}
validateJs = String
.format(HTML,
formSelector,
rules.toString(),
messages.toString(),
StringHelp.IsNullOrEmpty(errLocationJsHandler) ? ErrorPlacementHandlerDefault
: String.format(ErrorPlacementHandlerCustom,
errLocationJsHandler), "ori")
.replace("\r\n", "")
// 清除 空格 tab缩进
.replaceAll("[ ]{2,}", "")
+ "\r\n" + ERROR_CLASS;
RedisUtil.setString(redisKey, validateJs);
return validateJs;
}



}

声明自定义EL

<?xml version="1.0" encoding="GBK"?>  
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<!-- 定义函数版本 -->
<tlib-version>1.0</tlib-version>
<!-- 定义函数名称 -->
<short-name>okwei</short-name>
<!-- 定义第一个函数 -->
<function>
<!-- 生成验证代码 不支持自定义错误显示位置 -->
<name>validate</name>
<!-- 定义函数处理类 -->
<function-class>com.okwei.weiconnect.wap.tag.OkweiEL</function-class>
<!-- 定义函数的对应方法 -->
<function-signature>
java.lang.String validate(java.lang.String,java.lang.String,java.lang.String,java.lang.String)
</function-signature>
</function>

</taglib>


二、 定义 form pojo类

 ort com.opensymphony.xwork2.validator.annotations.StringLengthFieldValidator;

/*
* 登录提交信息
*/
public class RegForm {



@RequiredStringValidator(message = "手机不能为空")
public String getPhone() {
return phone;
}

public void setPhone(String phone) {
this.phone = phone;
}

@RequiredStringValidator(message = "密码不能为空")
@StringLengthFieldValidator(minLength = "6", message = "请输入至少6位密码")
public String getPwd() {
return pwd;
}

public void setPwd(String pwd) {
this.pwd = pwd;
}

@RequiredStringValidator(message = "请输入店铺名")
public String getWeiname() {
return weiname;
}

public void setWeiname(String weiname) {
this.weiname = weiname;
}



@RequiredStringValidator(message = "验证码错误")
public String getVcode() {
return vcode;
}

public void setVcode(String vcode) {
this.vcode = vcode;
}





/*
* 手机号
*/
private String phone;

/*
* 密码
*/
private String pwd;



/*
* 验证码
*/
private String vcode;

/*
* 昵称
*/
private String weiname;


}

 三、jsp中使用

<article>
<div class="w fl">
<form:form action="" method="post" modelAttribute="regform">
<div class="w fl bg-w p20">
<div class="w fl line-b inpsue1">
<div class="fl pusetrr1 imgtp1">
<img src='${okweiel:imgcdn("images/ig_logs1.png")}' height="25">
</div>
<div class="pusetrr2">
<form:input path="tel" placeholder="输入注册手机号" />
</div>
</div>
<div class="w fl line-b inpsue1">
<div class="fl pusetrr1 imgtp3">
<img src='${okweiel:imgcdn("images/ig_logs3.png" )}' height="20">
</div>
<div class="pusetrr2">
<form:password path="pwd" placeholder="设置登录密码" />
</div>
</div>
<div class="w fl line-b inpsue1">
<div class="fl pusetrr1 imgtp2">
<img src='${okweiel:imgcdn("images/ig_logs2.png")}' height="15">
</div>
<div class="fl pusetrr3">
<form:input path="vcode" placeholder="输入验证码" />
</div>
<div class="fr pusetrr4" id="btnsc">获取验证码</div>
</div>

</div>

<div class="w fl pl20 pr20 mt15">
<div class="w fl uqdiser">
<input type="submit" value="提交">
</div>
</div>
<form:errors path="*" cssClass="formerr"></form:errors>
<div class="w fl mt30 tc f14">
<a href="${pageContext.request.contextPath }/logreg" title="已注册账号绑定微信" class="cB">已有账号,去登录></a>
</div>
<div class="w fl wx_logins pr mt30">
<div class="pa wxin_tile f12 c9">使用微信快速登录</div>
</div>

<div class="w fl wxin_dlus mt15 mb15">
<a href="${pageContext.request.contextPath}/logreg/wxbind"><img
src='${okweiel:imgcdn("images/wx_logins.png")}' width="60"></a>
</div>

</form:form>
</div>
</article>
<!--关键语句-->
${okweiel:validate("xxx.form.RegisterForm","","#regform","0006")}

四、controller中使用

/**
* 显示注册页
*
* @param model
* @return
*/
@RequestMapping("reg")
public String reg(Model model) {
model.addAttribute("regform", new RegisterForm());
return "loginReg/reg";
}


/**
* 提交注册
*
* @param form
* @param formValid
* @param model
* @param redirectAttributes
* @return
*/
@RequestMapping(value = "reg", method = RequestMethod.POST)
public String doReg(@Valid @ModelAttribute("regform") RegisterForm form,
BindingResult formValid, Model model, HttpSession session) {
// 检查手机 检查验证码 交给service
if (formValid.hasErrors()) {
return "loginReg/reg";
}
ReturnModel loginModel = loginService.userRegist(form.getTel(),
form.getVcode(), "", form.getPwd());
if (loginModel.getStatu() == ReturnStatus.Success) {


return "redirect:/logreg";
} else {
formValid
.addError(new ObjectError("regerror", loginModel.getMsg()));
return "loginReg/reg";
}
}
五、效果图

spring mvc前端验证代码生成器

spring mvc前端验证代码生成器