JSR303 data verification Spring unified exception handling

Posted by Kodak07 on Tue, 22 Feb 2022 00:50:58 +0100

JSR303 performs data verification

1. Add verification annotation to Bean
Multiple annotations can be verified

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

//Cannot be empty + custom rule
@NotEmpty 
@Pattern(regexp = "!/^[a-zA-Z]$/",message = "The search initial must be a letter")
private String firstLetter;

//Cannot be empty + minimum
@NotEmpty 
@Min(value = 0,message = "The minimum sorting value is 0")
private Integer sort;


2. Turn on the data verification function when submitting data

    /**
     * preservation
     * @Valid Annotations tell spring MVC that validation is required
     */
    @RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand) {
        brandService.save(brand);

        return R.ok();
    }

3. There will be a default response in case of verification error
The verified Bean can be followed by BindResult to obtain the verification result

/**
     * preservation
     * @Valid Annotations tell spring MVC that validation is required
     * BindingResultresult The verification results are encapsulated
     */
    @RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand, BindingResultresult)  {
        brandService.save(brand);

        return R.ok();
    }

Test:

@RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result)  {
        if (result.hasErrors()){
            Map<String,String> map = new  HashMap<>();
            //Get the error result of verification
            result.getFieldErrors().forEach((item) -> {
                //Error message
               String message =  item.getDefaultMessage();
                //Get wrong field
               String field =  item.getField();
                map.put(field, message);
            });

           return R.error(400,"The submitted data is illegal").put("data",map);
        }else {

        }
        brandService.save(brand);

        return R.ok();
    }

Return results

{
	"msg": "The submitted data is illegal",
	"code": 400,
	"data": {
		"name": "Brand name cannot be empty"
	}
}

Unified exception handling class

@ControllerAdvice

  • ControllerAdvice is essentially a Component
  • It's an implementation of aop thinking. You told me you need interception rules, and I'll help you stop them. Specifically, you want to do more detailed interception screening and processing after interception, which are defined by @ ExceptionHandler, @ InitBinder or @ ModelAttribute annotations and the methods annotated by them.
  • @ControllerAdvice cooperates with @ ExceptionHandler to realize global exception handling
  • Global data preprocessing
  • The data in the class modified by global data binding @ ControllerAdvice can be accessed by each controller
//Put data
@ControllerAdvice
public class MyGlobalExceptionHandler {
    @ModelAttribute(name = "md")
    public Map<String,Object> mydata() {
        HashMap<String, Object> map = new HashMap<>();
        map.put("age", 99);
        map.put("gender", "male");
        return map;
    }
}
//Fetch data
@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(Model model) {
        Map<String, Object> map = model.asMap();
        System.out.println(map);
        int i = 1 / 0;
        return "hello controller advice";
    }
}

1. Extract an exception handling class

package cn.cloud.xmall.product.exception;

import cn.cloud.xmall.common.exception.ExceptionEnume;
import cn.cloud.xmall.common.utils.R;
import lombok.extern.slf4j.Slf4j;
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;

/**
 * Centralized processing of all exceptions
 * The exception of the specified Controller will be intercepted when thrown
 * basePackages Which Controller exception to handle
 */
@Slf4j
@RestControllerAdvice(basePackages = "cn.cloud.xmall.product.controller")
public class XmallExceptionControllerAdvice {

    /**
     * This method handles data verification exceptions
     * @ExceptionHandler Tell spring MVC what exceptions this method marked with this annotation can handle
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleVailException(MethodArgumentNotValidException e){

        BindingResult result = e.getBindingResult();
        log.error("Data verification exception"+e.getMessage()+e.getClass());
        Map<String,String> map = new HashMap<>();
            //Get the error result of verification
            result.getFieldErrors().forEach((item) -> {
                //Error message
               String message =  item.getDefaultMessage();
                //Get wrong field
               String field =  item.getField();
                map.put(field, message);
            });

        return R.error(ExceptionEnume.VATLD_EXCEPTION.getCode(),ExceptionEnume.VATLD_EXCEPTION.getMsg()).put("data",map);
    }

    /**
     * Maximum exception handling any type
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public R handleException(){

        return R.error(ExceptionEnume.UNKNOW_EXCEPTION.getCode(),ExceptionEnume.UNKNOW_EXCEPTION.getMsg());
    }
}

Define exception information enumeration class

package cn.cloud.xmall.common.exception;

/**
 * System error code enumeration class
 */
public enum ExceptionEnume {
    UNKNOW_EXCEPTION(10001,"System fault"),
    VATLD_EXCEPTION(10001,"Parameter format verification failed");

    private Integer code;
    private String msg;

    ExceptionEnume(Integer code,String msg){
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

Data grouping verification

The verification rules triggered in different cases can be distinguished by group verification
It can complete complex verification of multiple scenarios

@Valid annotation for JSR303 specification @ Validated annotation for spring Framework

1. Mark the verification notes. Under what circumstances do you need to verify

@NotNull(message = "Modification must be specified id",groups = {UpdateGroup.class})
@Null(message = "Brand cannot be specified when adding id",groups = {AddGroup.class})
@TableId
private Long brandId;

@Validated can specify one or more validation groups

2. Modify the @ Valid annotation to @ Validated and specify the verification group (multiple can be specified)

@RequestMapping("/save")
    public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand)  {
    }

By default, no group verification annotation is specified. For example, @ NULL will not take effect in the case of group verification, but only in the case of no group

Custom annotation verification

The existing annotations cannot meet the verification requirements

1. Write user-defined verification notes

Dependency needs to be introduced

<!--   check API     -->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
package cn.cloud.xmall.common.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;

/**
 * Just copy other verification annotations
 */
@Documented
//Custom validator object
@Constraint(validatedBy = { ListValueConstraintValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {

    //The variable is the full class name of the verification annotation. This parameter must be
    String message() default "{cn.cloud.xmall.common.valid.ListValue.message}";
    //This parameter must be
    Class<?>[] groups() default { };
    //This parameter must be
    Class<? extends Payload>[] payload() default { };

    //The value attribute is empty by default
    int[] vals() default { };

}

2. Write a custom verifier

@Constraint (validatedby = {}) validatedby = {} can specify what to use for verification

Public interface constraintvalidator < a extends annotation, t > is an interface generic type. The first is a doomed annotation. The second specifies what type of data to verify. The user-defined verifier needs to implement this interface

package cn.cloud.xmall.common.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<>();

    /**
     * Initialization method
     * The details of @ ListValue annotation will be obtained
     * vals That is @ ListValue(vals = {0,1})
     * @param constraintAnnotation
     */
    @Override
    public void initialize(ListValue constraintAnnotation) {
       int[] vals = constraintAnnotation.vals();
        //Save the verification information on the annotation into the Set
        if (vals.length != 0){
            for (int val : vals) {
                set.add(val);
            }
        }

    }

    /**
     * Judge whether the verification is successful
     * @param value  The value to be verified is passed
     * @param context Context of the entire verification
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        //Returns whether the specified value is included
        return set.contains(value);
    }
}

3. Associate custom validators and annotations

Add a custom verifier to the verification annotation
Multiple validators can be used. For example, the types to be verified are different
@Constraint(validatedBy = { ListValueConstraintValidator.class })

Topics: Java Spring intellij-idea