springboot2.1入门系列二 hibernate validator

本文为Spring Boot2.1系列的第二篇,代码可以从github下载 https://github.com/yinww/demo-springboot2.git

对数据验证的工作经常由前端工程师完成,但是代码重复繁琐,并且即使有前端的验证也不能放松后端对数据的验证工作,否则可能存在数据安全性的问题,代码示意如下:

function checkForm() {
	var username = $('#name').val();
	if (null == username || '' == username) {
		$('#errMsg').html(getI18n('userCantEmpty'));
		return false;
	}

	var code = $('#code').val();
	if (null == code || '' == code) {
		$('#errMsg').html(getI18n('codeCantEmpty'));
		return false;
	}
	return true;
}

本文介绍Spring Boot的后台验证技术hibernate validator,内容包括:

1、hibernate validator验证

2、hibernate validator自定义验证

3、hibernate validator验证的国际化

一、hibernate validator验证

Spring Boot2.1自带hibernate validator包,不需要额外单独引入。

参考 上一篇文章 访问https://start.spring.io/ 创建工程demo002,Group输入com.yinww.demo.springboot2.demo002 , Artifact输入demo0022,Dependencies选择Web和Thymeleaf,然后点击Generate Project按钮下载zip包,解压缩并将其作为Maven Project引入到Eclipse环境中。

新建JavaBean类User,写明对每个属性的验证要求,如不能为空等等

package com.yinww.demo.springboot2.demo002.domain;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;

public class User {

	@NotBlank(message = "用户名不能为空")
	private String username;

	@NotBlank(message = "密码不能为空")
	private String password;

	@Email(message = "邮箱格式错误")
	private String email;
	
    // getter and setter ...
}

新建一个返回值的类ResultEntity

package com.yinww.demo.springboot2.demo002.domain;

public class ResultEntity {

	private int status;
	private Object data;
	private String msg;
	
	public ResultEntity() {
	}
	
	public ResultEntity(int status) {
		this.status = status;
	}
	
	public ResultEntity(int status, Object data) {
		this.status = status;
		this.data = data;
	}
    // getter and setter...
}

创建一个Controller类TestController,其中save方法的@Validated注解就是给user对象做数据校验的

package com.yinww.demo.springboot2.demo002.controller;

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.yinww.demo.springboot2.demo002.domain.ResultEntity;
import com.yinww.demo.springboot2.demo002.domain.User;

@Controller
public class TestController {

	@RequestMapping(value={"/", "index.html"})
	public String index() {
		return "index";
	}

	@RequestMapping(value="save")
	@ResponseBody
	public Object save(@Validated User user) {
		return new ResultEntity(HttpStatus.OK.value(), user);
	}
}

编写一个全局的异常处理类TestExceptionHandler 以捕捉异常并返回到前端

package com.yinww.demo.springboot2.demo002.handler;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.yinww.demo.springboot2.demo002.domain.ResultEntity;

@ControllerAdvice
public class TestExceptionHandler {

	private static final Logger log = LoggerFactory.getLogger(TestExceptionHandler.class);

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResultEntity handleException(Exception e){
        log.error(e.getMessage(), e);

        BindingResult result = null;
        if (e instanceof MethodArgumentNotValidException){
            result = ((MethodArgumentNotValidException) e).getBindingResult();
        } else if (e instanceof BindException){
            result = ((BindException) e).getBindingResult();
        } else if (e instanceof ConstraintViolationException){
            Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) e).getConstraintViolations();
            List<String> errors = new ArrayList<>();
            for (ConstraintViolation<?> violation : constraintViolations) {
            	errors.add(violation.getMessage());
            }
            return new ResultEntity(HttpStatus.OK.value(), errors);
        }
        
        if (result != null) {
        	List<String> errors = new ArrayList<>();
            for (ObjectError error : result.getAllErrors()) {
            	errors.add(error.getDefaultMessage());
            }
            return new ResultEntity(HttpStatus.BAD_REQUEST.value(), errors);
        }

        return new ResultEntity(HttpStatus.BAD_REQUEST.value());
    }
}

创建一个Thymeleaf模板文件index.html

<form action="save" method="post">
   <table>
     <tr>
       <td>用户名</td>
       <td><input name="username"></td>
     </tr>
     <tr>
       <td>密码</td>
       <td><input type="password" name="password"></td>
     </tr>
     <tr>
       <td>邮箱</td>
       <td><input name="email"></td>
     </tr>
     <tr>
       <td><input type="submit"> </td>
     </tr>
   </table>
</form>

运行Spring Boot工程demo002,在浏览器中访问 http://localhost:8080/ 

点击提交按钮,如果验证结果有问题,那么页面返回验证出错的内容

{"status":400,"data":["密码不能为空","用户名不能为空"],"msg":null}

前端工程师可以对上面的json内容进行解析,展示成想要的效果。

二、自定义验证

给User类添加一个身份证号码的属性,验证身份证号码的格式是否正确,required用于说明是否必填,相关的逻辑都需要在后面的自定义验证类中实现

@IDConstraint(required = false, message = "身份证号码格式错误")
private String idCard;

创建注解验证约束类 IDConstraint

package com.yinww.demo.springboot2.demo002.validator;

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

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

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Constraint(validatedBy=IDValidator.class)
public @interface IDConstraint {
	boolean required();
	
	String message();
	
	Class<?>[] groups() default {};
	
	Class<? extends Payload>[] payload() default {};
}

创建验证的逻辑类IDValidator

package com.yinww.demo.springboot2.demo002.validator;

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

public class IDValidator implements ConstraintValidator<IDConstraint, String> {
	private boolean required;
	
	@Override
	public void initialize(IDConstraint idConstraint) {
		this.required = idConstraint.required();
	}

	@Override
	public boolean isValid(String value, ConstraintValidatorContext context) {
		if(value == null || value.trim().length() == 0) {
			return !required;
		}
		
		String regIdcard = "^\\d{6}(19|20)\\d{2}((1[0-2])|0\\d)([0-2]\\d|30|31)\\d{3}[\\d|X|x]$";
		return value.matches(regIdcard);
	}

}

修改Thymeleaf模板index.html form表单中加上身份证号码输入框

<form action="save" method="post">
   <table>
     <tr>
       <td>用户名</td>
       <td><input name="username"></td>
     </tr>
     <tr>
       <td>密码</td>
       <td><input type="password" name="password"></td>
     </tr>
     <tr>
       <td>邮箱</td>
       <td><input name="email"></td>
     </tr>
     <tr>
       <td>身份证号码</td>
       <td><input name="idCard"></td>
     </tr>
     <tr>
       <td><input type="submit"> </td>
     </tr>
   </table>
</form>

运行Spring Boot工程demo002,在浏览器中访问 http://localhost:8080/ 

身份证号码输入框中输入1234,点击提交按钮,如果验证结果有问题,那么页面返回验证出错的内容

{"status":400,"data":["密码不能为空","用户名不能为空","身份证号码格式错误"],"msg":null}

前端工程师可以对上面的json内容进行解析,展示成想要的效果。

三、hibernate validator的国际化

国际化,简单说就是需要将内容抽取到多语言的properties文件中。

修改application.properties文件,添加内容:

spring.messages.basename=i18n/messages

在resources目录下创建i18n目录,在i18n目录下创建messages.properties、messages_zh_CN.properties和messages_en_US.properties 三个文件。messages_zh_CN.properties文件中输入:

error.username=用户名不能为空
error.password=密码不能为空
error.email=邮箱格式错误
error.idcard=身份证号码格式错误

messages_en_US.properties文件中输入:

error.username=username cannot be empty
error.password=password cannot be empty
error.email=email invalid
error.idcard=idcard invalid

messages.properties内容空着就行,但是不能没有这个文件。

User类修改为:

public class User {

	@NotBlank(message = "{error.username}")
	private String username;

	@NotBlank(message = "{error.password}")
	private String password;

	@Email(message = "{error.email}")
	private String email;
	
	@IDConstraint(required = false, message = "{error.idcard}")
	private String idCard;

    // getter and setter
}

Demo002Application类修改为

@SpringBootApplication
public class Demo002Application {

	@Autowired
    private MessageSource messageSource;

	public static void main(String[] args) {
		SpringApplication.run(Demo002Application.class, args);
	}
	
	@Bean
    public Validator getValidator() throws Exception {
        LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
        validator.setValidationMessageSource(messageSource);
        return validator;
    }

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver slr = new SessionLocaleResolver();
        slr.setDefaultLocale(Locale.CHINA);
//        slr.setDefaultLocale(Locale.US);
        return slr;
    }

}

运行Spring Boot工程demo002,在浏览器中访问 http://localhost:8080/  也能正常验证。将上面代码中的Locale设置为Locale.US即可显示messages_en_US.properties文件中定义的英文的验证错误内容。

本文内容到此结束,更多内容可关注公众号:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值