33-SpringBoot JSR303数据校验

使用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)    验正NumberString对象是否小于等于指定的值
@Min(value)    验正NumberString对象是否大于等于指定的值
@DecimalMax(value)    被标注的值必须不大于约束中指定的最大值。这个约束的参数是一个通过 Big Decimal定义的最大值的字符串表示,小数存在精度
@DecimalMin(value)    被标注的值必须不小于约束中指定的最小值。这个约束的参数是一个通过 Big Decimal定义的最小值的字符串表示,小数存在精度
@Digits(integer, fraction)    验证字符串是否是符合指定格式的数字, Interger指定整数精度, fraction指定小数精度
@Size(min, max)    验证对象(ArrayCollectionMapString)长度是否在给定的范围之内
@Past    验证DateCalendar对象是否在当前时间之前
@Future    验证DateCalendar对象是否在当前时间之后
@Pattern    验证 String对象是否符合正则表达式的规则
@NotBlank    检查约束字符串是不是Null,被Trim的长度是否大于0。只对字符串,且会去掉前后空格
@URL    验证是否是合法的url
@Email    验证是否是合法的邮件地址
@CreditCardNumber    验证是否是合法的信用卡号码
@Length(min, max)    验证字符串的长度必须在指定的范围内
@NotEmpty    检査元素是否为NULL或者 EMPTY。用于ArrayCollectionMapString
@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"
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值