web接口往往需要做参数校验,hibernate-validator包中提供了很多注解,譬如Length、Email、NotBlank、Range等注解为我们提供了特定的校验功能。但是一些个性化的校验以上注解就无能为力了,譬如校验两次输入的密码是否一致。但是ScriptAssert为我们提供了个性化检验参数的能力。
以用户注册为例。在用户注册时我们要校验输入密码与确认密码是否一致,和用户名中是否包含特殊字符
@ScriptAssert(script = "_this.password.equals(_this.rePassword)",lang = "javascript",message = "确认密码与输入密码不一致")
上面是校验对象中password属性与rePassword属性是否相等,equals返回true表示校验通过,返回false表示校验不通过(会返回错误信息)。
_this是默认的对象别名,lang="javascript" 表示用java 执行script中的脚本
script指定特定的静态方法校验用户名是否包含特殊字符
@ScriptAssert(script = "com.wl.account.dto.RegisterUserDto.checkUserName(_this.userName)",lang = "javascript",
message = "用户名不能包含! @ # $ % & * \\ / ? ?特殊符号")
public class RegisterUserDto {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public static boolean checkUserName(String userName){
String[] chars = {"!","@","#","$","%","&","*","\\","/","?","?"};
for(String c : chars){
if(userName.contains(c)){
return false;
}
}
return true;
}
}
注意script中方法是一个静态的方法,且包含完整的调用路径。校验参数时,会调用CheckUserName这个静态方法,方法参数为这个对象的userName属性。
多个ScriptAssert需要使用@ScriptAssert.List
完整的例子如下
校验参数的dto
package com.wl.account.dto;
import com.wl.account.common.model.UserVo;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.ScriptAssert;
/**
* Created by wl on 2021/4/17.
*/
//@ScriptAssert(script = "_this.password==_this.rePassword",lang = "javascript",message = "确认密码与输入密码不一致")
@ScriptAssert.List({
@ScriptAssert(script = "com.wl.account.dto.RegisterUserDto.checkUserName(_this.userName)",lang = "javascript",
message = "用户名不能包含! @ # $ % & * \\ / ? ?特殊符号"),
@ScriptAssert(script = "_this.password.equals(_this.rePassword)",lang = "javascript",message = "确认密码与输入密码不一致")
})
public class RegisterUserDto {
@NotBlank(message = "用户名不能为空")
@Length(message = "用户名长度应在3-12位",max = 12,min = 3)
private String userName;
@NotBlank(message = "密码不能为空")
@Length(message = "密码长度应在6-15位",max = 15,min = 6)
private String password;
@NotBlank(message = "确认密码不能为空")
private String rePassword;
private String mobile;
@Email
private String email;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRePassword() {
return rePassword;
}
public void setRePassword(String rePassword) {
this.rePassword = rePassword;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public static boolean checkUserName(String userName){
String[] chars = {"!","@","#","$","%","&","*","\\","/","?","?"};
for(String c : chars){
if(userName.contains(c)){
return false;
}
}
return true;
}
public UserVo toUserVo(){
UserVo userVo = new UserVo();
userVo.setUserName(this.getUserName());
userVo.setPassword(this.getPassword());
userVo.setEmail(this.getEmail());
userVo.setMobile(this.getMobile());
return userVo;
}
}
注册接口
@RequestMapping("sign")
public Result sign(@Validated RegisterUserDto dto, BindingResult result,HttpServletRequest request){
if(result.getErrorCount() > 0){
return Result.fail(result.getAllErrors().get(0).getDefaultMessage());
}
//注册逻辑
return Result.success();
}
前端注册输入含有特殊字符的用户名
返回响应错误信息
使用ExceptionHandler注解捕获异常,这样代码中就不用声明BindingResult
我的ExceptionHandler如下(可根据自身需求返回特定的错误信息,可以通过ExceptionHandler注解的value值捕获特定的异常)
@ResponseBody
@ExceptionHandler(value = Exception.class)
public Result handlerException(Throwable t){
logger.error("throwable: ",t);
return Result.fail(getExceptionMsg(t));
}
private String getExceptionMsg(Throwable t){
StringBuilder baseMsg = t.getMessage() != null ? new StringBuilder(t.getMessage()) : new StringBuilder(t.toString());
if(t.getCause() != null){
if(t.getCause().getCause() != null) {
if (t.getCause().getCause().getMessage() != null) {
baseMsg.append("\n").append(t.getCause().getCause().getMessage());
}
}
}
if(t.getStackTrace() != null && t.getStackTrace().length > 0){
StackTraceElement[] elements = t.getStackTrace();
for(StackTraceElement element : elements){
if(element.getClassName().contains("com.wl.account")){
baseMsg.append("\n")
.append("className:").append(element.getClassName())
.append("\n").append("methodName:").append(element.getMethodName())
.append("\n").append("lineNumber:").append(element.getLineNumber());
break;
}
}
}
return baseMsg.toString();
}
将上述代码放入BaseController中,所有继承BaseController的对象都将获得捕获异常的能力
返回错误