在前后端传递数据的时候,往往后端需要校验传递数据的格式,比如用户名的格式,密码是否为空。我们可以在service层编写代码判断,但是当我们在多处需要校验传递来的数据的时候,就会出现大量重复的代码,一旦出错,就需要多处修改,非常麻烦,而且这样我们的软件将会非常槽糕。这时,我们可以用Hibernate Validation的注解来进行校验,十分的方便简洁,Hibernate Validation是Spring自带的校验框架,在javax.validation包下可以找到。下面我们来详细讲解一下Hibernate Validation的使用以及如何自定义注解来处理数据的校验。
一、使用Hibernate Validation校验注解
如何使用Hibernate Validation呢?我们来假设这样一个场景,我们需要创建一个用户,这时后端需要校验前台传来的数据。下面我们来看看这个实例怎么编写代码。
首先,用MockMvc来伪造请求进行测试,这里为了方便不编写前端代码了。
@Test
public void whenPostSuccess() throws Exception {
Date date=(Date) new java.util.Date();
System.out.println(date.getTime());
String content="{\"username\":\"shinelon\",\"password\":null,\"birthday\":"+date.getTime()+"}";
String result=mockMvc.perform(post("/user")
.content(content)
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andReturn().getResponse().getContentAsString();
System.out.println(result);
}
上面的代码传递了一段json字符串到后台,与表单提交不同的是,表单提交的参数会自动封装到用户实体类中,但是json字符串不会封装到实体类中,那怎么办呢?这时我们需要用到@RequestBody注解,在参数前加入这个参数,它会自动将json字符串按照映射到相应的字段中,并且当后端处理好业务后返回到前端的数据也会被处理成json字符串,这就给我们的开发带来了很好好处。
我们接着上面的场景,前端创建用户,后台来接收参数进行校验:
Controller层代码:
@PostMapping("/user")
public User create(@Valid @RequestBody User user,BindingResult errors) {
if(errors.hasErrors()) {
errors.getAllErrors().forEach(error->System.out.println(error.getDefaultMessage()));;
}
user.setId(1);
System.out.println(user.getId());
System.out.println(user.getUsername());
System.out.println(user.getPassword());
System.out.println(user.getBirthday());
return user;
}
在上面的代码中,我们会看到@Valid注解和BindingResult errors,这就是我们的正题Hibernate Validation注解,当我们在参数user前面加上@Valid注解时,它就会在该类上自动校验相应的字段,当然User类中肯定需要对字段进行限制校验格式,下面我们会说明。我们再看看最后一个参数BindingResult,这个参数就是当你前台传递的数据校验不通过的时候处理一些错误信息的。如果没有BindingResult参数,当传递来的数据在@Valid注解中校验不通过的时候,它会直接返回错误的状态给用户,这也许和不友好,并且我们有些时候需要收集一些日志,比如记录你输入的一些错误信息,这时就需要用到BindingResult参数,它会在校验不通过的时候任然进入URL映射的方法中进行一些出来,这时我们可以打印具体的错误信息给用户以友好的展示,必须要说明的一点是,这个参数必须要在@Valid注解的参数的后面。
下面我们来看看在user类中的注解。
public class User {
private int id;
public String username;
@NotBlank
public String password;
private Date birthday;
//省略get,set方法
我们以密码不为空来校验数据,在之前的代码中,我们password设置为null,肯定校验失败,不过我们加了BindingResult参数,它会进入方法体中打印错误日志,下面是打印的错误日志:
上面是Hibernate Validation内部自带的默认错误信息,有时候需要设置自定义的错误信息,这时我们就可以使用message参数来指定。
public class User {
private int id;
public String username;
//使用这个注解表示前台传回来的密码不能为空
@NotBlank(message="密码不能为空") //这个注解是hibernate validator中提供的开源项目
public String password;
@Past(message="生日的日期必须是过去的时间") //表示生日必须是过去的时间
private Date birthday;
下面是测试代码,Controller层的代码不变,还是上面的:
@Test
public void whenPostSuccess() throws Exception {
//一年以后的时间
Date date=(Date) new java.util.Date(LocalDateTime.now().plusYears(1).
atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
System.out.println(date.getTime());
String content="{\"username\":\"shinelon\",\"password\":null,\"birthday\":"+date.getTime()+"}";
String result=mockMvc.perform(post("/user")
.content(content)
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andReturn().getResponse().getContentAsString();
System.out.println(result);
}
运行结果我们会发现打印了自定义的错误信息:
上面是Hibernate Validation几个简单的注解示例,下面这张图是Hibernate Validation的注解详解:
有些的读者可以自行试验上面的注解,这里就介绍到这里,下面我们来看看如何自定义注解。
二、自定义校验注解
有些场景Hibernate Validation不足以满足我们的需求,这时我们需要自定义注解来校验代码。
如何自定义注解呢?
首先,我们需要编写一个注解类:
MyValidator.java:
package cn.shinelon.annotation;
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;
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MyConstraintValidator.class)
public @interface MyValidator {
//自定义注解必须实现这三个属性
String message();
Class<?>[] groups() default{};
Class<? extends Payload>[] payload() default{};
}
然后编写该注解要处理校验的类
MyConstraintValidator.java:
/**
*
*/
package cn.shinelon.annotation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;
import cn.shinelon.service.impl.HelloServiceImpl;
/**
* @author Shinelon
*
*/
public class MyConstraintValidator implements ConstraintValidator<MyValidator, Object> {
@Autowired
public HelloServiceImpl helloServiceImpl;
@Override
public boolean isValid(Object arg0, ConstraintValidatorContext arg1) {
helloServiceImpl.hello(" "+arg0);
System.out.println(arg0);
//返回true或者false表示是否校验成功
return false;
}
//初始化
@Override
public void initialize(MyValidator arg0) {
System.out.println("my validator init");
}
}
该类实现了ConstraintValidator
public interface HelloService {
public void hello(String name);
}
实现类:
@Service
public class HelloServiceImpl implements HelloService {
/* (non-Javadoc)
* @see cn.shinelon.service.HelloService#hello()
*/
@Override
public void hello(String name) {
System.out.println("hello"+name);
}
}
定义好注解后,我们将注解加载username字段上进行测试,因为注解中isValid方法返回的false,就是说校验失败。
@MyValidator(message="这是一个自定义注解")
public String username;
然后我们继续运行上面的单元测试,可以看到我们的自定义注解生效。
至此,我们就给大家介绍完Hibernate Validation校验注解的使用以及自定义注解来进行校验。欢迎留言讨论。