JSR303 校验框架的使用

很多时候,用户提交的表单数据需要验证其合法性。

比如不能为空,电话号码,邮箱格式等等,这些一般在js里面会有一次校验,选择表单input type也能帮助校验。

但是前端校验并不能保证到后台的数据就一定正确,在后台我们同样需要做校验。

这里我们来学习如何使用JSR303验证框架来校验表单/ajax提交的参数。

一,JSR 303是Java EE 6中的一项子规范,叫做Bean Validation,官方参考实现是hibernate Validator,此实现与Hibernate ORM没有任何关系。JSR 303用于对Java Bean中的字段的值进行验证。

这里我们引入依赖包:

  
	org.hibernate  
	hibernate-validator  
	5.2.4.Final  

二,构建映射实体类,UserInfoRequest

package com.maven.web.entity;

import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;

import com.maven.web.validator.NotRepeat;

/**
 * 接收前端传递数据的映射类
 * @author Administrator
 *
 */
public class UserInfoRequest {
	
	@NotNull
	private String userName;

	@Size(min=6, max=8)
    private String password;

	@Email
    private String email;

    private String phone;

	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 getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPhone() {
		return phone;
	}

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

	@Override
	public String toString() {
		return "UserInfoRequest [userName=" + userName + ", password=" + password + ", email=" + email + ", phone="
				+ phone + "]";
	}
    
    

}
这里我们只对userName,password,email三个字段添加校验

三,构建测试类,ValidController

package com.maven.web.controller;

import javax.annotation.Resource;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.maven.web.common.ErrorCode;
import com.maven.web.common.Result;
import com.maven.web.entity.UserInfoRequest;
import com.maven.web.validator.UserInfoValidator;

/**
 * 对前端提交参数进行校验
 * @author Administrator
 *
 */

@RestController
@RequestMapping("/valid")
public class ValidController{
	
	private Logger logger =  LoggerFactory.getLogger(this.getClass());
	
	/**
	 * 校验表单提交数据,或ajax模拟表单提交数据
	 * 没有添加参数BindingResult result,如果校验不通过则直接抛异常
	 * @param userInfo
	 * @return
	 */
	@RequestMapping(value="/form/post",method=RequestMethod.POST)
	public Result validForm(@Valid UserInfoRequest userInfo){
		logger.info("如果校验通过则打印参数:"+userInfo);
		Result r = new Result(ErrorCode.SUCCESS);
		return r;
	}
	
	/**
	 * 校验ajax传递的json字符串
	 * @param userInfo
	 * @return
	 */
	@RequestMapping(value="/ajax/post",method=RequestMethod.POST)
	public Result validAjax(@RequestBody @Valid UserInfoRequest userInfo){
		logger.info("如果校验通过则打印参数:"+userInfo);
		Result r = new Result(ErrorCode.SUCCESS);
		return r;
	}
	

}

这里我分成了表单提交,和ajax json字符串两种方式进行测试,json字符串需要添加注解@RequestBody才能被转换成实体bean

值得注意的是,注解@Valid要紧挨着需要校验的实体bean.

一般情况下,在需要校验的实体Bean后面还需要添加参数,BindingResult result,如下:

	@RequestMapping(value="/form/post",method=RequestMethod.POST)
	public Result validForm(@Valid UserInfoRequest userInfo,BindingResult result){
		if (result.hasErrors()) {
			return new Result(ErrorCode.PARAM_ERROR);
		}
		logger.info("如果校验通过则打印参数:"+userInfo);
		Result r = new Result(ErrorCode.SUCCESS);
		return r;
	}
BindingResult 的作用是当参数不合法时能够捕捉到错误,不会直接抛异常。

鉴于我们之前有学习过如何处理公共异常,这里我们就让程序抛出异常,统一在公共异常中处理。

关于异常的处理,可以跳转这里查看:http://blog.csdn.net/mynoteblog/article/details/70046671

下面我直接贴出相关代码:

(1) Result

package com.maven.web.common;

import java.io.Serializable;

public class Result implements Serializable{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 3998962529381113102L;

	private int code;
	
	private String msg;
	
	private String detailMsg;

	public Result() {
		
	}

	public Result(int code, String msg, String detailMsg) {
		this.setCode(code);
		this.setMsg(msg);
		this.setDetailMsg(detailMsg);
	}

	public Result(ErrorCode errorCode) {
		this(errorCode.getCode(), errorCode.getMessage(), "");
	}
	
	public Result(ErrorCode errorCode, String detailMsg) {
		this(errorCode.getCode(), errorCode.getMessage(), detailMsg);
	}
	
	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	
	public String getDetailMsg() {
		return detailMsg;
	}

	public void setDetailMsg(String detailMsg) {
		this.detailMsg = detailMsg;
	}

	public void setCode(ErrorCode errorCode) {
		this.setCode(errorCode.getCode());
		this.setMsg(errorCode.getMessage());
	}

}

(2) ErrorCode

package com.maven.web.common;

/**
 * 错误码。
 * 
 */
public enum ErrorCode {
	
	SUCCESS(0, "success"),
	SYSTEM_ERROR(9999, "system error"),
	UNKNOWN_ERROR(1000, "unknown error"),
	PARAM_ERROR(1003, "parameter error"),
	REQUEST_ERROR(1004, "request error");
	
	private int code;
	private String message;

	ErrorCode(int code, String message) {
		this.code = code;
		this.message = message;
	}

	public int getCode() {
		return code;
	}

	public String getMessage() {
		return message;
	}
	
}

(3) DefaultExceptionHandler

package com.maven.web.exception;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.validation.UnexpectedTypeException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.maven.web.common.ErrorCode;
import com.maven.web.common.Result;
import com.maven.web.entity.ValidFieldError;

/**
 * 异常处理类。
 * 
 */
@ControllerAdvice
public class DefaultExceptionHandler{

	private final static Logger logger = LoggerFactory.getLogger(DefaultExceptionHandler.class);
	
	/**
	 * 参数不合法错误
	 * 
	 * @param e
	 * @return
	 */
	@ExceptionHandler(IllegalArgumentException.class)
	@ResponseBody
	public Result illegalArgumentException(IllegalArgumentException e) {
		logger.error(e.getMessage());
		return new Result(ErrorCode.PARAM_ERROR);
	}

	/**
	 * 缺少请求参数错误
	 * 
	 * @param e
	 * @return
	 */
	@ExceptionHandler(MissingServletRequestParameterException.class)
	@ResponseBody
	public Result MissingServletRequestParameterException(MissingServletRequestParameterException e) {
		logger.error(e.getMessage());
		return new Result(ErrorCode.PARAM_ERROR);
	}

	/**
	 * 请求类型错误
	 * 
	 * @param e
	 * @return
	 */
	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	@ResponseBody
	public Result httpRequestMethodNotSupportedException(Exception e) {
		logger.error(e.getMessage());
		return new Result(ErrorCode.REQUEST_ERROR);
	}

	/**
	 * 参数校验错误
	 * form表单提交是BindException
	 * ajax json字符提交是MethodArgumentNotValidException
	 * @param e
	 * @return
	 */
	@ExceptionHandler(BindException.class)
	@ResponseBody
	public Result bindException(Exception e) {
		logger.error(e.getMessage());
		BindingResult bindingResult = (e instanceof BindException) ? ((BindException)e).getBindingResult() 
				: ((MethodArgumentNotValidException)e).getBindingResult();
		if (bindingResult != null && bindingResult.hasErrors()) {
			List errors = bindingResult.getFieldErrors();
			List validList = new ArrayList();
			if (!(CollectionUtils.isEmpty(errors))) {
				Iterator localIterator = errors.iterator();
				while (localIterator.hasNext()) {
					FieldError fe = (FieldError) localIterator.next();
					validList.add(new ValidFieldError(fe));
				}
			}
			logger.error("参数校验错误:"+validList.toString());
			return new Result(ErrorCode.PARAM_ERROR, validList.toString());
		}
		return new Result(ErrorCode.SYSTEM_ERROR);
	}

	/**
	 * 其他异常
	 * @param e
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	@ResponseBody
	public Result defaultException(Exception e) {
		logger.error(e.getMessage());
		if ((e instanceof BindException) || (e instanceof MethodArgumentNotValidException) || (e instanceof UnexpectedTypeException)) {
			return bindException(e);
		}
		return new Result(ErrorCode.SYSTEM_ERROR);
	}

}

(4) ValidFieldError

package com.maven.web.entity;

import java.io.Serializable;

import org.springframework.validation.FieldError;

/**
 * 校验错误实体类
 * 
 * @author Administrator
 *
 */
public class ValidFieldError implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 5901446595499292806L;
	private String field;
	private String message;
	private String code;

	public ValidFieldError(FieldError fieldError){
	    setCode(fieldError.getDefaultMessage());
	    setField(fieldError.getField());
	    setMessage(fieldError.getDefaultMessage());
	  }

	public String getField() {
		return field;
	}

	public void setField(String field) {
		this.field = field;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	@Override
	public String toString() {
		return "参数"+field+"错误:"+message;
	}
	
	
	
}

准备工作做好了,下面我们来测试:

1) http://localhost:8088/com.maven.web/valid/form/post

JSR303 校验框架的使用_第1张图片

2)http://localhost:8088/com.maven.web/valid/ajax/post

JSR303 校验框架的使用_第2张图片

==================================================================================

上面我们使用的是默认的校验规则,很多时候,我们需要根据具体业务自定义校验规则,下面我们来看如何写自己的定义注解。

一,自定义注解,定义一个校验用户名不能重复的注解

package com.maven.web.validator;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target( { METHOD, FIELD, ANNOTATION_TYPE })  
@Retention(RUNTIME)  
@Constraint(validatedBy = UserNameValidator.class)  
@Documented 
public @interface NotRepeat {
	
	String message() default "用户名已存在";
	
	Class[] groups() default {};  
    
    public abstract Class[] payload() default {}; 

}
二,在上面自定义的注解中,我们定义了一个UserNameValidator.class,用来处理校验逻辑
package com.maven.web.validator;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/**
 * 校验用户名
 * @author Administrator
 *
 */
public class UserNameValidator implements ConstraintValidator {

	public void initialize(NotRepeat constraintAnnotation) {
		// TODO Auto-generated method stub
		
	}

	/**
	 * 自定义用户名校验规则
	 * 假设数据库已有用户名"aa",如果用户再次提交相同姓名,则提示用户名重复
	 */
	public boolean isValid(String value, ConstraintValidatorContext context) {
		if (value.equals("aa")) {
			return false;
		}
		return true;
	}

}
三,将自定义的注解加到字段userName上面
	@NotNull
	@NotRepeat
	private String userName;
再次测试: http://localhost:8088/com.maven.web/valid/form/post

JSR303 校验框架的使用_第3张图片


可以看到返回的提示消息,用户名未通过校验。

****************************************************************

除了JSR303验证方法,我们还可以使用spring validator,来自定义自己的校验规则。两者同时使用并不矛盾。

下面我们来看:

一,定义一个java类,实现Validator接口

package com.maven.web.validator;

import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

import com.maven.web.entity.UserInfoRequest;
import com.maven.web.util.SpringUtils;

/**
 * 使用spring validator校验数据
 * @author Administrator
 *
 */
@Component
public class UserInfoValidator implements Validator{

	/**
	 * 这里绑定需要校验的java bean
	 */
	public boolean supports(Class clazz) {
		return UserInfoRequest.class.equals(clazz);
	}

	/**
	 * 校验逻辑
	 * 可以同时对多个属性进行校验
	 */
	public void validate(Object target, Errors errors) {

		String msg = SpringUtils.getMessage("邮箱不能为空");
		ValidationUtils.rejectIfEmpty(errors, "email",null,msg);
		
		UserInfoRequest userInfo = (UserInfoRequest) target;
		
		if (!userInfo.getPassword().equals("000000")) {
			errors.rejectValue("password",null, SpringUtils.getMessage("用户名密码错误"));
		}
	}

}
这里我们定义两个规则,一是email不能为空,二是password不等于"000000"时表示未登录

规则定义了,下面是放到需要校验的请求里面使用。

在controller中定义initBinder方法,绑定上面我们自定义的校验规则。

package com.maven.web.controller;

import javax.annotation.Resource;
import javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.maven.web.common.ErrorCode;
import com.maven.web.common.Result;
import com.maven.web.entity.UserInfoRequest;
import com.maven.web.validator.UserInfoValidator;

/**
 * 对前端提交参数进行校验
 * @author Administrator
 *
 */

@RestController
@RequestMapping("/valid")
public class ValidController{
	
	private Logger logger =  LoggerFactory.getLogger(this.getClass());
	
	@Resource
	private UserInfoValidator userInfoValidator;
	
	/**
	 * 绑定UserInfoValidator
	 * spring validator 添加对UserInfo的校验  
	 * @param webDataBinder
	 */
	@InitBinder  
	public void initBinder(WebDataBinder webDataBinder){
	    webDataBinder.addValidators(userInfoValidator);  
	} 
	
	/**
	 * 校验表单提交数据,或ajax模拟表单提交数据
	 * 没有添加参数BindingResult result,如果校验不通过则直接抛异常
	 * @param userInfo
	 * @return
	 */
	@RequestMapping(value="/form/post",method=RequestMethod.POST)
	public Result validForm(@Valid UserInfoRequest userInfo){
		logger.info("如果校验通过则打印参数:"+userInfo);
		Result r = new Result(ErrorCode.SUCCESS);
		return r;
	}
	
	/**
	 * 校验ajax传递的json字符串
	 * @param userInfo
	 * @return
	 */
	@RequestMapping(value="/ajax/post",method=RequestMethod.POST)
	public Result validAjax(@RequestBody @Valid UserInfoRequest userInfo){
		logger.info("如果校验通过则打印参数:"+userInfo);
		Result r = new Result(ErrorCode.SUCCESS);
		return r;
	}
	

}
下面我们再来测试: http://localhost:8088/com.maven.web/valid/form/post

JSR303 校验框架的使用_第4张图片

可以看到,校验不通过的提示又多了一条,密码不正确


大家可以自行测试,下篇将接着介绍spring 国际化消息处理,会在本篇博文上进行扩展。








你可能感兴趣的:(JSR303,hibernate,technology)