One, foreword

At present, there are various forms of parameter verification in the project. If some interface parameters are verified one by one, it is too tedious and the code readability is very poor. Some use validation annotations that come with Spring Boot, but only at the surface. Therefore, unified sorting has been done here for the convenience of everyone’s understanding and subsequent use.

  • In the project, it is necessary to verify the received data (non-null, length, format, etc.), whether it is the object data submitted by the front-end page form and the interface docking with the third-party company or the parameter verification of Excel import in the project. Front-end JS verification can cover most of the verification accusations, but in order to avoid users bypass the browser and use HTTP tools to directly request some illegal data back end, the server data verification is also necessary to prevent dirty data falling into the database. At the same time, the primitive writing method in the back end is to use if to check one by one (there are many fields). It is too tedious to check interface parameters one by one by code, and the code readability is very poor. The following describes how to integrate a parameter Validator into SprinBoot, as well as advanced techniques for parameter validation (custom validation, grouping validation).

This article relies on an existing code base, and a global exception verifier has been added to the project. Local code uploaded to remote repository github.com/BenjaminFya…

Second, Java Bean validation basis

An overview of the

  • Before using it in your project, you need to cover the basics of the standard framework JSR 380 (also known as Bean Validation 2.0) for validating Java beans.
  • Validating user input is a very common requirement in most applications. The Java Bean Validation framework has become the de facto standard for handling this logic.

JSR 380

  • JSR 380 is the Java API specification for bean validation. This ensures that the bean’s properties meet certain conditions, using annotations such as @notnull, @min, and @max
  • This version requires Java 8 or later and takes advantage of new features added in Java 8, such as type annotation and support for new types such as Optional and LocalDate.
  • For complete information on the specification, continue to read JSR 380

Rely on and validate apis

<! According to the JSR 380 specification, validation-API dependencies contain standard validation apis.
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1. The Final</version>
</dependency>

<! Hibernate Validator is a reference implementation of the validation API.
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.13. The Final</version>
</dependency>
Copy the code

Use validation annotations

Here we use a Userbean to add some simple validation

package com.java.xval.val.beanvalidation;

import lombok.Data;

import javax.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;

@Data
public class User {

    @notnull (message = "name cannot be empty ")
    private String name;

    @AssertTrue
    private boolean working;

    @size (min = 10, Max = 200, message = "the number of characters should be between 10 and 200 inclusive ")
    private String aboutMe;

    @min (value = 18, message = "be at least 18 years old ")
    @max (value = 150, message = "should not be older than 150 ")
    private int age;

    @email (message = "Email should be valid ")
    private String email;

    private List<@notblank (message = "Note cannot be blank ") String> preferences;

    @past (message = "date of birth must be a Past time ")
    private LocalDate dateOfBirth;

    @decimalmin (value = "0.0", inclusive = false, message = "payment amount not less than 0")
    @digits (integer = 4, fraction = 2, message = "Payment amount must be less than {INTEGER} Digits and no more than {fraction} decimal places ")
    private BigDecimal price;
    
}

Copy the code

All the annotations used in the example are standard JSR annotations:

Common constraints are annotated as follows

Validation annotations The type of data to validate instructions
@AssertFalse Boolean, boolean Verify that the element value of the annotation is false
@AssertTrue Boolean, boolean Verify that the element value of the annotation is true
@NotNull Any type Verify that the element value of the annotation is not NULL
@Null Any type Verify that the element value of the annotation is NULL
@ Min (value = value) BigDecimal, BigInteger, byte, short, int, Long, and any Number or CharSequence (which stores numbers) subtypes Verify that the element value of the annotation is greater than or equal to the value specified by @min
@max (value= value) Same as @min Verify that the element value of the annotation is less than or equal to the value specified by @max
@ DecimalMin (value = value) Same as @min Verify that the element value of the annotation is greater than or equal to the value specified by @decimalmin
@ DecimalMax (value = value) Same as @min Verify that the element value of the annotation is less than or equal to the value specified by @decimalmax
@Digits(integer= integer number, fraction= decimal number) Same as @min Validates the upper limit of integer and decimal digits for the element value of the annotation
@size (min= lower limit, Max = upper limit) String, Collection, Map, array, etc Validates the element value of an annotation within the specified min and Max (inclusive) interval, such as character length and collection size
@Past java.util. Date, java.util. Calendar; The date type of the Joda Time class library Verify that the element value (date type) of the annotation is earlier than the current time
@Future Same as @past Verify that the element value (date type) of the annotation is later than the current time
@NotBlank CharSequence subtype Verify that the element value of the annotation is not null (not null, length 0 after removing the first whitespace), unlike @notempty, where @notBlank applies only to strings and removes the first whitespace of strings when comparing
@length (min= lower limit, Max = upper limit) CharSequence subtype Verify that the element value length of the annotation is within min and Max
@NotEmpty CharSequence subtype, Collection, Map, array Verify that the element value of the annotation is not null and is not empty (string length is not 0, collection size is not 0)
@range (min= min, Max = Max) BigDecimal, BigInteger, CharSequence, byte, short, int, long, and other atomic and wrapper types Verify that the element value of the annotation is between the minimum and maximum value
@email (regexp= regular expression, flag= pattern of flags) CharSequence subtype (e.g. String) Verify that the element value of the annotation is Email, or you can specify a custom Email format using regEXP and flag
@pattern (regexp= regular expression, flag= Pattern of flags) String, the subtype of any CharSequence Verifies that the element value of the annotation matches the specified regular expression
@Valid Any non-atomic type For example, if the user object has an address object attribute, if you want to validate the address object with the user object, add the @VALID annotation to the address object to cascade the authentication
@Positive Same as @min Apply to numerical values and verify that they are strictly positive
@PositiveOrZero Same as @min Applies to values and verifies that they are strictly positive, including 0
@Negative Same as @min Apply to numerical values and verify that they are strictly negative numbers
@NegativeOrZero Same as @min Applies to values and verifies that they are strictly negative, including 0
@PastOrPresent java.util. Date, java.util. Calendar; The date type of the Joda Time class library Verify that the date value is past or past, including the present; You can apply to date types, including those added in Java 8.
@FutureOrPresent java.util. Date, java.util. Calendar; The date type of the Joda Time class library Verify that date values are in the future, including the present

Validation annotations can also be applied to elements of a collection

    private List<@notblank (message = "Note cannot be blank ") String> preferences;
Copy the code

Support for new in Java 8Optional type

    @past (message = "date of birth must be a Past time ")
    private LocalDate dateOfBirth;

    public Optional<@Past LocalDate> getDateOfBirth() {
        return Optional.of(dateOfBirth);
    }
Copy the code

Program verification

  • Validation in frameworks (such as Spring) through the use of annotations is now primarily set up in code through unit tests
package com.java.xval.val;

import com.java.xval.val.beanvalidation.User;
import org.junit.Before;
import org.junit.Test;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Set;

public class ValidationTest {

    private Validator validator;

    @Before
    public void setup(a) {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    @Test
    public void ifNameIsNull(a) {
        User user = new User();
        user.setWorking(true);
        user.setAboutMe("me");
        user.setAge(50);

        // validate method to verify that constraints defined in our UserBean User object will be returned as sets
        Set<ConstraintViolation<User>> violations = validator.validate(user);

        for (ConstraintViolation<User> violation : violations) {

            // The getMessage method gets all the offending messagesSystem.out.println(violation.getMessage()); }}}Copy the code
  • An example is shown in the figure below

conclusion

  • Above, you’ll see a simple pass-through of the Java validation API, the basics of bean validation using javax.Validation annotations and the API, and the implementation of the code snippet can be found on GitHub

Differences between @notnull, @Notempty, and @NotBlank constraints in Bean validation

An overview of the differences

The above overall use of bean validation implementation, relatively simple, but some implementation of these constraints still have related differences, when used in the project often see people use more confusing.

  • @notnull a constrained CharSequence, Collection, Map, or Array is valid as long as it is NotNull, but it can be null.
  • @notempty a constrained CharSequence, Collection, Map, or Array is valid as long as it is NotEmpty and its size/length is greater than zero.
  • The @notBlank constraint strings are valid as long as they are not empty and the trimmed length is greater than zero.

Fourth, SpringBoot integration parameter verification

Introduction Outlines

  • Spring Boot provides powerful support for this common but critical task of validating user input
  • Although Spring Boot supports seamless integration with custom validators, the de facto standard for performing validation is Hibernate Validator

Application layer

Java business applications come in different forms and types. Based on these standards and forms, our program needs to determine which layers are required for parameter validation.

  • The consumer layer, or Web layer, is the top layer of the Web application and is primarily responsible for user input and providing corresponding responses. The Web layer is the entry point to the application, responsible for authentication and serving as the first line of defense against unauthorized users.

  • Service layer authentication

The service layer is the layer in the application that communicates before the Web layer and persistence layer, and the business logic resides in the server, including authentication logic and so on. When validation is not bound to the Web layer, any validator available is allowed. Also, the input of client data is not always controlled by the WebREST layer. Without validation at the service layer, where standard Java JSR-303 validation can also be used, data can flow to the persistence layer, causing problems

package com.java.xval.val.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.java.xval.val.common.utils.ValidatorUtil;
import com.java.xval.val.mapper.PmsSkuStockMapper;
import com.java.xval.val.model.PmsSkuStock;
import com.java.xval.val.service.PmsSkuStockService;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.validation.*;
import java.util.Set;

/** ** <p> * SkU's inventory service implementation class * </p> **@author fangyang
 * @sinceThe 2021-09-16 * /
@Service
public class PmsSkuStockServiceImpl extends ServiceImpl<PmsSkuStockMapper.PmsSkuStock> implements PmsSkuStockService {

    @Override
    public void addPmsSkuStock(PmsSkuStock pmsSkuStock) {

        Set<ConstraintViolation<PmsSkuStock>> validate = ValidatorUtil.getValidator().validate(pmsSkuStock);

        if(! CollectionUtils.isEmpty(validate)) { StringBuilder sb =new StringBuilder();
            for (ConstraintViolation<PmsSkuStock> constraintViolation : validate) {
                sb.append(constraintViolation.getMessage());
            }
            throw new ConstraintViolationException("Error occurred: "+ sb, validate); } save(pmsSkuStock); }}Copy the code

SpringBoot Rest validation starts to introduce dependencies

Starting with Boot 2.3, we also need to explicitly add spring-boot-starter-validation dependencies:

<dependency>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-starter-web</artifactid>
</dependency>

<dependency>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-starter-validation</artifactid>
</dependency>
Copy the code

Defines the entity class for parameter validation

package com.java.xval.val.model;

import java.math.BigDecimal;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;

import java.io.Serializable;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.validation.constraints.*;

/** * Sku inventory **@author fangyang
 * @sinceThe 2021-09-16 * /
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("pms_sku_stock")
@apiModel (value = "PmsSkuStock object ", description = "SKu inventory ")
public class PmsSkuStock implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    private Long productId;

    @apiModelProperty (value = "SKU code ")
    @notblank (message = "SKU encoding cannot be blank ")
    private String skuCode;

    @notnull (message = "Payment amount cannot be empty ")
    @decimalmin (value = "0.0", inclusive = false, message = "payment amount not less than 0")
    @digits (integer = 4, fraction = 2, message = "Payment amount must be less than {INTEGER} Digits and no more than {fraction} decimal places ")
    private BigDecimal price;

    @positiveorZero (message = "Inventory must not be less than 0")
    @apiModelProperty (value = "inventory ")
    private Integer stock;

    @apiModelProperty (value = "ApiModelProperty ")
    @positive (message = "Alert stock must be greater than 0")
    private Integer lowStock;

    @apiModelProperty (value = "sales attribute 1")
    private String sp1;

    private String sp2;

    private String sp3;

    @apiModelProperty (value = "display image ")
    private String pic;

    @apiModelProperty (value = "volume ")
    @positiveorZero (message = "Inventory cannot be negative ")
    private Integer sale;

    @apiModelProperty (value = "product promotion price ")
    @decimalmin (value = "0", message = "Single item promotion price must be greater than 0")
    private BigDecimal promotionPrice;

    @apiModelProperty (value = "lock inventory ")
    @min (value = 0, message = "Lock stock must be greater than 0")
    private Integer lockStock;

}

Copy the code

Here I list the inventory table for the skU of an item and show how to use Bean Validation to constrain the properties of the Bean object. This is the basic definition of an inventory property for an item.

Implement a REST controller

You need to implement a layer that allows you to get the values assigned to the commodity inventory constraint fields, which can further validate them to perform the next task based on the validation results. Spring Boot makes this seemingly complex process very simple with the implementation of the REST controller

package com.java.xval.val.controller;

import com.java.xval.val.common.api.CommonResult;
import com.java.xval.val.model.PmsSkuStock;
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;

/** * SkU inventory front-end controller **@author fangyang
 * @sinceThe 2021-09-16 * /
@RestController
@RequestMapping("/val/pmsSkuStock")
public class PmsSkuStockController {

    @PostMapping("/add")
    CommonResult<String> addPmsSkuStock(@Valid @RequestBody PmsSkuStock pmsSkuStock) {

        return CommonResult.success("pmsSkuStock is valid"); }}Copy the code
  • The implementation of the addPmsSkuStock() method in Spring REST is to increase the commodity inventory information, and the most relevant part of the validation parameter process is the use of the @valid annotation.
  • When Spring Boot finds a parameter with the @VALID annotation, it automatically boots the default JSR380 implementation of Hibernate Validator — and validates the parameter. When the target parameter validation fails, a Spring Boot thrown MethodArgumentNotValidException anomalies

@ExceptionHandler

  • The @ExceptionHandler annotation allows us to handle a particular type of exception through a single method
package com.java.xval.val.common.exception;

import com.java.xval.val.common.api.CommonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
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;

/** * Global exception handling */

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public CommonResult<String> handleValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        return getCommonResult(bindingResult);
    }

    /** * Abnormal data encapsulation. **@paramBindingResult validation framework *@return the CommonResult
     */
    private CommonResult<String> getCommonResult(BindingResult bindingResult) {
        String message = null;
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            if(fieldError ! =null) { message = fieldError.getField() + fieldError.getDefaultMessage(); }}returnCommonResult.validateFailed(message); }}Copy the code
  • Specify MethodArgumentNotValidException as to deal with the anomaly. Therefore, SpringBoot calls this method when the specified PmsSkuStock object is invalid.
  • This method encapsulates invalid fields and validation error messages that are intercepted by GlobalExceptionHandler and then populates the error message in the custom object CommonResult as a JSON representation that is returned to the client for further processing.
  • In summary, the REST controller allows you to process requests for different situations and validate that the merchandise inventory object is returned in JSON format.

Testing REST Controllers

Verify using unit tests

  • Currently springBoot only needs to test to the Web layer, using the @webMvctest annotation. Allow methods implemented by the MockMvcRequestBuilders and MockMvcResultMatchers classes to unit test requests and responses
package com.java.xval.val;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;

@RunWith(SpringRunner.class)
@WebMvcTest
@AutoConfigureMockMvc
public class PmsSkuStockControllerIntegrationTest {

    @Resource
    private MockMvc mockMvc;

    @Test
    public void pmsSkuStock(a) throws Exception {
        MediaType textPlainUtf8 = new MediaType(MediaType.TEXT_PLAIN, StandardCharsets.UTF_8);
        String pmsSkuStock = "{\"price\": \"-12\", \"skuCode\" : \"bob\"}";
        mockMvc.perform(MockMvcRequestBuilders.post("/val/pmsSkuStock/add")
                .content(pmsSkuStock)
                .contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content()
                        .contentType(textPlainUtf8));
    }
}
Copy the code
  • The unit tests are as follows

{“code”:404, “message”:”price payment not less than 0″, “data”:null}

Use Postmen to test the REST controller API

  • If the coverage of unit testing is not required in the company’s project, unit testing is required (PS: I think that if unit testing is necessary, it can reduce the bug rate of the code and find the problems in the project immediately. But I personally use Postmen for most of my unit testing except for the necessary logical complex modules.)

Use the postMen test to return a price payment that cannot be less than 0

5. The difference between the @VALID and @validated annotations

Valid and Validated annotations

  • Valid(javax.validation) : a standard annotation in Bean Validation that validates the field/method/input parameter that needs validation
  • @ Validated (org. Springframework. Validation. The annotation) : is the Spring of @ Valid extended variant, support group

Group validation code example

  • Start by developing a simple user order table using SpringBoot. We only have some basic user information as follows:
package com.java.xval.val.model.request;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Null;

/** ** order list */
@Data
@ApiModel
public class OrderDTO {

    @apiModelProperty ("id, new need not pass, change must pass ")
    @null (message = "ID must be Null or empty ")
    private Integer id;

    @APIModelProperty (value = "shopping account ", Required = true)
    @notblank (message = "Shopping account cannot be empty ")
    private String account;

    @apiModelProperty (value = "order number ")
    private String orderNum;

    @apiModelProperty (value = "mailbox ")
    @email (message = "message format is not correct ")
    private String email;

    @apiModelProperty (value = "address ")
    private String address;

    @apiModelProperty (value = "remarks ")
    private String remark;
}
Copy the code
  • REST controller. In this case, the addEmergency method with the @valid annotation is used to validate the order entered by the user
package com.java.xval.val.controller;

import com.java.xval.val.common.api.CommonResult;
import com.java.xval.val.model.request.OrderDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
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;

/** * Description: * < shopping account module > **@author fangyang
 * @sinceThe 2021-09-16 * /
@Slf4j
@API (tags = "[backend] order module ")
@RestController
@RequestMapping(value = "/admin/order")
public class OrderController {

    @PostMapping(value = "add")
    @ApiOperation(httpMethod = "POST", value = "添加", response = Boolean.class)
    public CommonResult<String> addEmergency(@ApiParam(required = true) @RequestBody @Valid OrderDTO orderDTO) {
        return CommonResult.success("Order added successfully"); }}Copy the code
  • If we need to extend this function at this time, we need to add a background administrator to modify the order remarks function. The same object needs to be extended to validate that the order ID and order remarks cannot be null when modified. To support this behavior, we need group validation and the @Validated comment.
  • We need to group the fields and create two different groups. First, you need to create two tag interfaces. Add (AddParam) and modify (UpdateParam) each group separately.
  • AddParam
package com.java.xval.val.model.request.validation;

import javax.validation.groups.Default;

public interface AddParam extends Default {}Copy the code
  • UpdateParam
package com.java.xval.val.model.request.validation;

import javax.validation.groups.Default;

public interface UpdateParam extends Default {}Copy the code
  • Order Bean entity class
package com.java.xval.val.model.request;

import com.java.xval.val.model.request.validation.AddParam;
import com.java.xval.val.model.request.validation.UpdateParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;

/** ** order list */
@Data
@ApiModel
public class OrderDTO {

    @apiModelProperty ("id, new need not pass, change must pass ")
    @null (message = "id must be Null or empty ", groups = {addparam.class})
    @notnull (message = "id cannot be empty ", groups = {updateparam.class})
    private Integer id;

    @APIModelProperty (value = "shopping account ", Required = true)
    @notblank (message = "shopping account cannot be empty ", groups = {addparam.class})
    private String account;

    @apiModelProperty (value = "order number ")
    private String orderNum;

    @apiModelProperty (value = "mailbox ")
    @email (message = "message format is not correct ")
    private String email;

    @apiModelProperty (value = "address ")
    private String address;

    @apiModelProperty (value = "remarks ")
    @notnull (message = "remarks cannot be empty ", groups = {updateparam.class})
    private String remark;
}

Copy the code
  • Modify the webController
package com.java.xval.val.controller;

import com.java.xval.val.common.api.CommonResult;
import com.java.xval.val.model.request.OrderDTO;
import com.java.xval.val.model.request.validation.AddParam;
import com.java.xval.val.model.request.validation.UpdateParam;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
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;

/** * Description: * < shopping account module > **@author fangyang
 * @sinceThe 2021-09-16 * /
@Slf4j
@API (tags = "[backend] order module ")
@RestController
@RequestMapping(value = "/admin/order")
public class OrderController {

    @PostMapping(value = "add")
    @ApiOperation(httpMethod = "POST", value = "添加", response = Boolean.class)
    public CommonResult<String> addEmergency(@ApiParam(required = true) @RequestBody @Validated({AddParam.class}) OrderDTO orderDTO) {
        return CommonResult.success("Order added successfully");
    }

    @PostMapping(value = "update")
    @apiOperation (httpMethod = "POST", value = "modify ", response = Boil.class)
    public CommonResult<String> updateEmergency(@ApiParam(required = true) @RequestBody @Validated({UpdateParam.class}) OrderDTO orderDTO) {
        return CommonResult.success("Order modification succeeded"); }}Copy the code
  • The above can be verified by itself. It can be seen that the use of @ is very important for group verification

Use the @valid annotation to mark nested objects

  • The @VALID annotation is used to mark especially nested attributes. This triggers validation of nested objects. For example, in our current scenario, let’s create an OrderNextCardDTO gift card collection object:
/** ** order list */
@Data
@ApiModel
public class OrderDTO {

    / /...

    @Valid
    @notnull (message = "Gift card cannot be empty ", groups = {addparam.class})
    @apiModelProperty (value = "Gift Card Collection ")
    List<OrderNextCardDTO> orderNextCardDTOList;

}

Copy the code
package com.java.xval.val.model.request;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;

/** * 

* Order subtable --> Gift card *

*/
@Data @ApiModel public class OrderNextCardDTO { @APIModelProperty (value = "gift card ID ") @notnull (message = "gift card id empty ", groups = {addparam.class}) private Integer cardId; @apiModelProperty (value = "value used ") @decimalmin (value = "0", message = "min 0", groups = {addparam.class}) private BigDecimal amount; } Copy the code
  • Added interface test legend

  • Modify interface test legend

conclusion

The @valid annotation guarantees validation of the entire object. Importantly, it performs validation of the entire object. This can cause problems for scenarios that require only partial validation. You can use @Validated to verify the group, including the above partial verification

Validation of enumerated types

Introduction to Validation enumeration

  • I’ll use custom annotations to build validation for enumerations. In JSR 380Bean validation annotations, most standard annotations cannot be applied to Enums
  • The @pattern annotation is also possible, but is not comprehensive enough for matching. The only standard annotations that can be applied to enumerations are @notnull and @NULL

Verifies that the string matches the enumerated value

  • Create an annotation to check if the string is valid for a particular enumeration.
package com.joyowo.smarthr.social.common.util.validator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** * a comment that verifies whether the state is within the specified range */
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidatorClass.class)
public @interfaceEnumValue { Class<? >[] groups()default {};

    Class<? extends Payload>[] payload() default {};

    / * * *@returnEnumeration class * /Class<? extends Enum<? >> enumClass();/ * * *@returnEnumerating the verification method */
    String enumMethod(a) default "isValidEnum";

    / * * *@returnThe default prompt text */
    String message(a) default"Parameter passing error, corresponding enumeration not found";

    / * * *@returnThe default return is false */
    boolean allowNull(a) default false;

}

Copy the code
  • You can add this annotation to the String field, and you can pass any enumerated value
/** ** order list */
@Data
@ApiModel
public class OrderDTO {

    / /...

    /**
     * 下单网站
     */
    @valueofenum (enumClass = OrderWebsiteEnum. Class, groups = {addParam.class}, message = "corresponding order website does not exist ")
    private String orderWebsite;

}

Copy the code
  • Define ValueOfEnumValidator to check if a string (or any CharSequence) is contained in an enum:
package com.java.xval.val.common.constraints.validator;

import com.java.xval.val.common.constraints.ValueOfEnum;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class ValueOfEnumValidator implements ConstraintValidator<ValueOfEnum.CharSequence> {

    private List<String> acceptedValues;

    @Override
    public void initialize(ValueOfEnum annotation) {

        Method getMessage;
        List<String> list = new ArrayList<>();
        try {
            getMessage = annotation.enumClass().getMethod(annotation.enumMethod());
            for (Enum<?> en : annotation.enumClass().getEnumConstants()) {
                String invoke = (String) getMessage.invoke(en);
                list.add(invoke);
                acceptedValues = list;
            }
        } catch (Exception e) {
            acceptedValues = null; }}@Override
    public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }
        if (acceptedValues == null) {
            return false;
        }

        returnacceptedValues.contains(value.toString()); }}Copy the code
  • The actual validation maps values to strings, not to enumerations. The defined validator is then used to check if it matches any enumerated values.

  • Enumerate interface test legends

Seven,

review

Parameter verification is frequently used in the current actual development, but still stays in simple use, such as grouping verification, custom annotation parameter verification has not been used, if the project to carry out object parameter verification, will establish more VO for receiving Create, Update scenarios, more honor and cumbersome. The instructions and use case tests are given above.

  • A variety of common validation annotations
  • Single parameter check
  • Global exception handling Automatic assembly check exception
  • Grouping validation
  • Nested validation
  • Custom annotation validation

Excel import and correlation parameter validation

  • Currently, excel import data source occupies a large proportion in the project, which can be used in the projectHibernate ValidatorValidate the underlying parameters, but for some associated parameters, such as policy change request import column validation
    • The date for stopping execution must be later than the execution start time
    • If the traceability object is not empty, it shall be consistent with the subject of payment
    • Fixed amount paid by the enterprise (YUAN) Charging frequency equal to monthly payment Subject equal to the enterprise or all This parameter is mandatory when and only when “monthly payment by the enterprise is equal to a fixed value”
    • Pay a fixed amount (yuan) fee frequency equal to pay by month subject equal to individual or all if and only if “pay by month rule equal to pay by fixed value”
    • Minimum base of enterprise payment (YUAN): the charging frequency is monthly. The calculation rule of monthly enterprise payment is equal to the proportion of the payment base, and must be filled in when the payment subject is equal to the enterprise or all
    • . And more than 100 logic to verify
  • In the face of this can use ali source project QLExpress script engine optimization, github address: github.com/alibaba/QLE… . In view of the current length, we will introduce the use of script engine in import separately in the future. If you are interested, you can know about it in advance.