使用JSR303需加入依赖
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
1.校验注解
注解 功能
@Notnull 验证对象是否不为mul,无法检査长度为0的字符串,于验证基本数据类型
@Null 验证对象是否为null
@AssertTrue 验证 Boolean对象是否为true
@AssertFalse 验证 Boolean对象是否为false
@Max(value) 验正Number和String对象是否小于等于指定的值
@Min(value) 验正Number和String对象是否大于等于指定的值
@DecimalMax(value) 被标注的值必须不大于约束中指定的最大值。这个约束的参数是一个通过 Big Decimal定义的最大值的字符串表示,小数存在精度
@DecimalMin(value) 被标注的值必须不小于约束中指定的最小值。这个约束的参数是一个通过 Big Decimal定义的最小值的字符串表示,小数存在精度
@Digits(integer, fraction) 验证字符串是否是符合指定格式的数字, Interger指定整数精度, fraction指定小数精度
@Size(min, max) 验证对象(Array、Collection、Map、 String)长度是否在给定的范围之内
@Past 验证Date和 Calendar对象是否在当前时间之前
@Future 验证Date和 Calendar对象是否在当前时间之后
@Pattern 验证 String对象是否符合正则表达式的规则
@NotBlank 检查约束字符串是不是Null,被Trim的长度是否大于0。只对字符串,且会去掉前后空格
@URL 验证是否是合法的url
@Email 验证是否是合法的邮件地址
@CreditCardNumber 验证是否是合法的信用卡号码
@Length(min, max) 验证字符串的长度必须在指定的范围内
@NotEmpty 检査元素是否为NULL或者 EMPTY。用于Array、Collection、Map、 String
@Range(min, max, message) 验证属性值必须在合适的范围内
2.简单校验
package com.atguigu.gulimall.mytest.entity;
import lombok.Data;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull;
@Data
public class Product {
@NotNull
private String name;
@NotNull
@Digits(integer = 10, fraction = 2)
private Double price;
}
package com.atguigu.gulimall.mytest.entity;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Data
public class Order {
// 必须不为 null, 大小是 10
@NotNull(message = "这是自定义错误信息, orderId不能为空")
@Size(min = 1, max = 10)
private String orderId;
// 必须不为空
@NotEmpty
private String customer;
// 必须是一个电子信箱地址
@Email
private String email;
// 必须不为空
@NotEmpty
private String address;
// 必须不为 null, 必须是下面四个字符串'created', 'paid', 'shipped', 'closed'其中之一
// @Status 是一个定制化的 contraint
@NotNull
private String status;
// 必须不为 null
// 嵌套验证, 加入@Valid注解才会验证product的属性
@Valid
private Product product;
}
package com.atguigu.gulimall.mytest.controller;
import com.atguigu.gulimall.mytest.entity.Order;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RequestMapping("/order")
@RestController
public class OrderController {
//加上@Valid 才会校验参数
@PostMapping("/save")
public Order save(@Valid @RequestBody Order order){
return order;
}
}
测试数据
{
"address": "string",
"customer": "string",
"email": "12345@qq.com",
"orderId": "",
"product": {
"name":"li",
"price":"6.66666"
},
"status": "11"
}
响应数据
{
"timestamp": "2022-07-23T13:45:01.201+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"Size.order.orderId",
"Size.orderId",
"Size.java.lang.String",
"Size"
],
"arguments": [
{
"codes": [
"order.orderId",
"orderId"
],
"arguments": null,
"defaultMessage": "orderId",
"code": "orderId"
},
10,
1
],
"defaultMessage": "个数必须在1和10之间",
"objectName": "order",
"field": "orderId",
"rejectedValue": "",
"bindingFailure": false,
"code": "Size"
},
{
"codes": [
"Digits.order.product.price",
"Digits.product.price",
"Digits.price",
"Digits.java.lang.Double",
"Digits"
],
"arguments": [
{
"codes": [
"order.product.price",
"product.price"
],
"arguments": null,
"defaultMessage": "product.price",
"code": "product.price"
},
2,
10
],
"defaultMessage": "数字的值超出了允许范围(只允许在10位整数和2位小数范围内)",
"objectName": "order",
"field": "product.price",
"rejectedValue": 6.66666,
"bindingFailure": false,
"code": "Digits"
}
],
"message": "Validation failed for object='order'. Error count: 2",
"trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.atguigu.gulimall.mytest.entity.Order com.atguigu.gulimall.mytest.controller.OrderController.save(com.atguigu.gulimall.mytest.entity.Order) with 2 errors: [Field error in object 'order' on field 'orderId': rejected value []; codes [Size.order.orderId,Size.orderId,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [order.orderId,orderId]; arguments []; default message [orderId],10,1]; default message [个数必须在1和10之间]] [Field error in object 'order' on field 'product.price': rejected value [6.66666]; codes [Digits.order.product.price,Digits.product.price,Digits.price,Digits.java.lang.Double,Digits]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [order.product.price,product.price]; arguments []; default message [product.price],2,10]; default message [数字的值超出了允许范围(只允许在10位整数和2位小数范围内)]] \r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:138)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:127)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:660)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\r\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:745)\r\n",
"path": "/order/save"
}
3.分组校验
例如, 对于一个对象属性, 在新增数据时需要类的全部属性均填写, 但是在修改数据时, 类的属性只需要填写部分. 这个时候我们就可以使用校验分组来解决这种情况. 可以把校验规则分成几组, 新增时的校验规则, 修改时的校验规则等等
分组接口
package com.atguigu.gulimall.mytest.valid;
public interface AddGroup {
}
package com.atguigu.gulimall.mytest.valid;
public interface UpdateGroup {
}
package com.atguigu.gulimall.mytest.entity;
import com.atguigu.gulimall.mytest.valid.AddGroup;
import com.atguigu.gulimall.mytest.valid.UpdateGroup;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
//一旦使用校验分组, 则全部属性都要给它分组, 否则该属性不会被校验
@Data
public class Order {
// 必须不为 null, 大小是 10
@NotNull(message = "这是自定义错误信息, orderId不能为空", groups = {AddGroup.class, UpdateGroup.class})
@Size(min = 1, max = 10)
private String orderId;
// 必须不为空
@NotEmpty(message = "新增时customer数据不能为空", groups = AddGroup.class)
private String customer;
// 必须是一个电子信箱地址
@Email(groups = {AddGroup.class, UpdateGroup.class})
@NotEmpty(message = "新增时email数据不能为空", groups = AddGroup.class)
private String email;
// 必须不为空
@NotEmpty(message = "新增时address数据不能为空", groups = AddGroup.class)
private String address;
}
package com.atguigu.gulimall.mytest.controller;
import com.atguigu.gulimall.mytest.entity.Order;
import com.atguigu.gulimall.mytest.valid.AddGroup;
import com.atguigu.gulimall.mytest.valid.UpdateGroup;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/order")
@RestController
public class OrderController {
@PostMapping("/save")
public Order save(@Validated({AddGroup.class}) @RequestBody Order order){
return order;
}
@PostMapping("/update")
public Order update(@Validated({UpdateGroup.class}) @RequestBody Order order){
return order;
}
}
测试新增
{
"address": "",
"customer": "",
"email": "",
"orderId": "1"
}
响应数据
{
"timestamp": "2022-07-23T14:06:23.695+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"NotEmpty.order.email",
"NotEmpty.email",
"NotEmpty.java.lang.String",
"NotEmpty"
],
"arguments": [
{
"codes": [
"order.email",
"email"
],
"arguments": null,
"defaultMessage": "email",
"code": "email"
}
],
"defaultMessage": "新增时email数据不能为空",
"objectName": "order",
"field": "email",
"rejectedValue": "",
"bindingFailure": false,
"code": "NotEmpty"
},
{
"codes": [
"NotEmpty.order.customer",
"NotEmpty.customer",
"NotEmpty.java.lang.String",
"NotEmpty"
],
"arguments": [
{
"codes": [
"order.customer",
"customer"
],
"arguments": null,
"defaultMessage": "customer",
"code": "customer"
}
],
"defaultMessage": "新增时customer数据不能为空",
"objectName": "order",
"field": "customer",
"rejectedValue": "",
"bindingFailure": false,
"code": "NotEmpty"
},
{
"codes": [
"NotEmpty.order.address",
"NotEmpty.address",
"NotEmpty.java.lang.String",
"NotEmpty"
],
"arguments": [
{
"codes": [
"order.address",
"address"
],
"arguments": null,
"defaultMessage": "address",
"code": "address"
}
],
"defaultMessage": "新增时address数据不能为空",
"objectName": "order",
"field": "address",
"rejectedValue": "",
"bindingFailure": false,
"code": "NotEmpty"
}
],
"message": "Validation failed for object='order'. Error count: 3",
"trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.atguigu.gulimall.mytest.entity.Order com.atguigu.gulimall.mytest.controller.OrderController.save(com.atguigu.gulimall.mytest.entity.Order) with 3 errors: [Field error in object 'order' on field 'email': rejected value []; codes [NotEmpty.order.email,NotEmpty.email,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [order.email,email]; arguments []; default message [email]]; default message [新增时email数据不能为空]] [Field error in object 'order' on field 'customer': rejected value []; codes [NotEmpty.order.customer,NotEmpty.customer,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [order.customer,customer]; arguments []; default message [customer]]; default message [新增时customer数据不能为空]] [Field error in object 'order' on field 'address': rejected value []; codes [NotEmpty.order.address,NotEmpty.address,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [order.address,address]; arguments []; default message [address]]; default message [新增时address数据不能为空]] \r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:138)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:127)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:660)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\r\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:745)\r\n",
"path": "/order/save"
}
测试修改
{
"address": "",
"customer": "",
"email": "123@qq.com",
"orderId": "1"
}
响应正常
{
"orderId": "1",
"customer": "",
"email": "123@qq.com",
"address": ""
}
4.自定义校验注解
校验注解我们也可以自定义, 使用我们自己的校验规则
自定义校验注解@ListValue
package com.atguigu.gulimall.mytest.valid;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set = new HashSet<>();
//初始化方法
@Override
public void initialize(ListValue constraintAnnotation) {
int[] vals = constraintAnnotation.vals();
for (int val : vals) {
set.add(val);
}
}
//判断是否校验成功
/**
*
* @param value 需要校验的值
* @param context
* @return
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
package com.atguigu.gulimall.mytest.valid;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
String message() default "必须提交指定的值";
//也可以在resources目录下创建一个ValidationMessages.properties文件
//内容是:com.atguigu.common.valid.ListValue.message=必须提交指定的值
//然后使用String message() default "{com.atguigu.common.valid.ListValue.message}";
//效果和上面一样,这是模仿SpringBoot框架代码的写法
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] vals() default { };
}
测试类
package com.atguigu.gulimall.mytest.entity;
import com.atguigu.gulimall.mytest.valid.AddGroup;
import com.atguigu.gulimall.mytest.valid.ListValue;
import com.atguigu.gulimall.mytest.valid.UpdateGroup;
import io.swagger.models.auth.In;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
//一旦使用校验分组, 则全部属性都要给它分组, 否则该属性不会被校验
@Data
public class Order {
// 必须不为 null, 大小是 10
@NotNull(message = "这是自定义错误信息, orderId不能为空")
@Size(min = 1, max = 10)
private String orderId;
@ListValue(vals = {0,1})
private Integer status;
}
package com.atguigu.gulimall.mytest.controller;
import com.atguigu.gulimall.mytest.entity.Order;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RequestMapping("/order")
@RestController
public class OrderController {
@PostMapping("/save")
public Order save(@Valid @RequestBody Order order){
return order;
}
}
运行测试
输入数据
{
"orderId": "1",
"status": 5
}
响应数据
{
"timestamp": "2022-07-23T14:18:25.794+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"ListValue.order.status",
"ListValue.status",
"ListValue.java.lang.Integer",
"ListValue"
],
"arguments": [
{
"codes": [
"order.status",
"status"
],
"arguments": null,
"defaultMessage": "status",
"code": "status"
},
[
0,
1
]
],
"defaultMessage": "必须提交指定的值",
"objectName": "order",
"field": "status",
"rejectedValue": 5,
"bindingFailure": false,
"code": "ListValue"
}
],
"message": "Validation failed for object='order'. Error count: 1",
"trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.atguigu.gulimall.mytest.entity.Order com.atguigu.gulimall.mytest.controller.OrderController.save(com.atguigu.gulimall.mytest.entity.Order): [Field error in object 'order' on field 'status': rejected value [5]; codes [ListValue.order.status,ListValue.status,ListValue.java.lang.Integer,ListValue]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [order.status,status]; arguments []; default message [status],[I@253ffcc6]; default message [必须提交指定的值]] \r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:138)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:127)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:660)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\r\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:745)\r\n",
"path": "/order/save"
}
5.BindingResult封装的校验错误信息
- @Valid 和 BindingResult 是一
一对应的,如果有多个@Valid,那么每个@Valid后面跟着的BindingResult就是这个@Valid的验证结果,顺序不能乱; - BindingResult bindingResult一定要紧跟在@Valid校验的实体类之后
package com.atguigu.gulimall.mytest.controller;
import com.atguigu.gulimall.mytest.entity.Order;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@RequestMapping("/order")
@RestController
public class OrderController {
@PostMapping("/save")
public Map<String, String> save(@Valid @RequestBody Order order, BindingResult bindingResult){
Map<String, String> map = new HashMap<>();
if (bindingResult.hasErrors()) {
bindingResult.getFieldErrors().forEach((item) -> {
String message = item.getDefaultMessage();
String field = item.getField();
map.put(field, message);
});
}
map.put("message", "order的自定义错误信息");
map.put("code", "6000");
return map;
}
}
运行测试
输入数据
{
"orderId": "1",
"status": 5
}
响应数据
{
"code": "6000",
"message": "order的自定义错误信息",
"status": "必须提交指定的值"
}
6.统一异常处理
使用@RestControllerAdvice进行异常处理类的封装,这样,在我们的controller中,就不用再try catch异常,全部由@RestControllerAdvice标注的异常处理类进行处理
package com.atguigu.gulimall.mytest.controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class ExceptionControllerAdvice {
/**
* 参数校验异常
*
* @param e
* @return
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Map<String, String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
Map<String, String> errorMap = new HashMap<>();
BindingResult bindingResult = e.getBindingResult();
bindingResult.getFieldErrors().forEach(fieldError -> {
String message = fieldError.getDefaultMessage();
String field = fieldError.getField();
errorMap.put(field, message);
});
errorMap.put("code", "4000");
errorMap.put("message", e.getMessage());
errorMap.put("class", e.getClass().toString());
return errorMap;
}
/**
* 处理其它未知异常
*
* @param e
* @return
*/
@ExceptionHandler(value = Throwable.class)
public Map<String, String> handleException(Throwable e) {
Map<String, String> errorMap = new HashMap<>();
log.error("出现未知异常:{},异常类型:{}", e.getMessage(), e.getClass());
errorMap.put("message", e.getMessage());
errorMap.put("code", "3000");
errorMap.put("class", e.getClass().toString());
return errorMap;
}
}
package com.atguigu.gulimall.mytest.controller;
import com.atguigu.gulimall.mytest.entity.Order;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@RequestMapping("/order")
@RestController
public class OrderController {
//加上@Valid 才会校验参数
@PostMapping("/save")
public Order save(@Valid @RequestBody Order order){
return order;
}
}
运行测试
输入数据
{
"orderId": "1",
"status": 5
}
响应数据
{
"code": "4000",
"message": "Validation failed for argument [0] in public com.atguigu.gulimall.mytest.entity.Order com.atguigu.gulimall.mytest.controller.OrderController.save(com.atguigu.gulimall.mytest.entity.Order): [Field error in object 'order' on field 'status': rejected value [5]; codes [ListValue.order.status,ListValue.status,ListValue.java.lang.Integer,ListValue]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [order.status,status]; arguments []; default message [status],[I@16e2499e]; default message [必须提交指定的值]] ",
"class": "class org.springframework.web.bind.MethodArgumentNotValidException",
"status": "必须提交指定的值"
}
输入数据
{
"orderId": "1",
"status": 5,,
}
响应数据
{
"code": "3000",
"message": "JSON parse error: Unexpected character (',' (code 44)): was expecting double-quote to start field name; nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character (',' (code 44)): was expecting double-quote to start field name\n at [Source: (PushbackInputStream); line: 3, column: 16]",
"class": "class org.springframework.http.converter.HttpMessageNotReadableException"
}