springboot-parameter verification

1 Introduction

What is parameter verification?

When we write the project, when we write the controller, we have written code like this more or less

@RequestMapping("/{studentId}")
public ResponseEntity<String> queryInfo(@PathVariable("studentId") String sudentId){
    
    

	if(!StringUtils.isEmpty(sudentId)){
    
    
		//....
	}
}

We need to verify the data sent from the front end, and then perform business operations...
Maybe you think, just verify the verification. But if there are a lot of data that we need to verify, there are ten methods for one method, and 100 methods like this...it will cause us to waste time on data verification, obviously we don't want this.

In the Controller layer, it is sometimes necessary to verify the input parameters of the interface. If it is implemented with its own verification logic code, there will be some drawbacks. One is that it will distract oneself and cannot allow one to concentrate on writing business logic code; The second is to make the verification logic code and business logic code coupled, and the code size is relatively bloated. In order to circumvent this situation, we can use Spring validation's Validated annotation to complete the interface parameter verification work, the following examples illustrate.

2. How to use

2.1 Add maven dependency

 <!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>1.1.0.Final</version>
    </dependency>

2.2 Add verification notes on the verification field

class Profile{
    
    
        @NotNull(message = "字段值不能为空")
        private String name;
        @NotNull
        private String sex;
        @Max(value = 20,message = "最大长度为20")
        private String address;
        @NotNull
        @Size(max=10,min=5,message = "字段长度要在5-10之间")
        private String fileName;
        @Pattern(regexp = "^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\\.[a-zA-Z0-9]{2,6}$",message = "不满足邮箱正则表达式")
        private String email;
        @AssertTrue(message = "字段为true才能通过")
        private boolean isSave;
        @Future(message = "时间在当前时间之后才可以通过")
        private Date date;
 
}

2.3 Use @Validated for validation at the Controller layer

@RequestMapping("file/upload")
 public void upload(@RequestPart("files") MultipartFile files, @Validated Profile profile, Errors error) throws IOException {
    
    
       if(error.hasErrors()){
    
    
           return;
       }
       files.transferTo(new File(files.getOriginalFilename()));保存文件
}

It can also be verified directly in the controller

  1. Controller turns on Validated verification
@RestController
@RequestMapping("/test")
@Validated
public class TestController{
    
    ...}
  1. Validate the parameters directly in the method
@GetMapping(value = "/method/{type}")
    public ResponseModel test(
    	@Pattern(regexp = "[1,2]",message = "类型只能为1或2")@PathVariable(value = "type")String types,
		@NotBlank(message="内容不能为空")String content,
        @Length(max = 64,message = "长度最大为64")String title,
        @NotEmpty(message = "集合不能为空")List<String> ids,
        @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$",message = "手机号格式有误") @NotBlank String mobile,
        @Max(value = 100,message = "最大值为100") @Min(value = 1,message = "最小值为1")Integer intValue) {
    
    ...}

2.4 What are the annotations

@Null The annotated element must be null
@NotNull The annotated element must not be null
@AssertTrue The annotated element must be true
@AssertFalse The annotated element must be false
@Min(value) The annotated element must be a number , Its value must be greater than or equal to the specified minimum value
@Max(value) The annotated element must be a number, and its value must be less than or equal to the specified maximum value
@DecimalMin(value) The annotated element must be a number, and its value must Greater than or equal to the specified minimum value
@DecimalMax(value) The annotated element must be a number, and its value must be less than or equal to the specified maximum value
@Size(max=, min=) The size of the annotated element must be within the specified range
@Digits (integer, fraction) The annotated element must be a number, and its value must be within the acceptable range
@Past The annotated element must be a date in the past
@Future The annotated element must be a date in the future
@ Pattern(regex=,flag=) The commented element must conform to the specified regular expression

2.5 Group check

Introduce this scenario: There are two different situations in editing and saving the user's account password and adding it.

Edit and modify -> save: only need to verify whether the username and password meet the conditions, no need to verify the id (because the id already exists in the database).

New -> Save: Added the need to verify whether the username and password meet the conditions, and also to verify the id.

At this time, groups are used to verify Bean attribute variables, which can also satisfy multiple verifications. Specifically, the following two steps are required

Step 1: Create a group interface class

The packet interface class is just a normal interface class and doesn’t make much sense. It is only used to identify under which circumstances this attribute is verified, which is similar tojava.io.Serializable

public interface addUser{
    
     

}

public interface editUser{
    
    

}

Step 2: Add xxx.class interface to Controller method parameters

When ID verification is performed on newly added users, an @Validated({addUser.class})interface class is added to indicate that the newly added users User.getId()need to be verified.

@Controller  
public class UserController {
    
      

  	@RequestMapping("/saveAdd")  
    public String saveAddUser(@Validated({
    
    addUser.class}) User user, BindingResult result) {
    
      

        if(result.hasErrors()) {
    
      
			return "error";  
        }  
        return "success";  
    }
}

Step 3: Add groups to Bean

Adding the groups grouping in the User entity class corresponds @NotEmpty(groups={addUser.class})to the UserController @Validated({addUser.class}), indicating that the saveAddUsernewly added user id will be verified only when the newly added user is executed.

public class User {
    
       

    //在分组addUser时,验证id不能为空,其他情况下不做验证
	@NotEmpty(groups={
    
    addUser.class})
    private String id;

    @NotEmpty(message = "用户名不能为空")
    private String username;

    @Size(min=6 ,max= 20 ,message = "密码最少6位,最高20位")
    private String password;
    ......
}

The above three steps can simply complete group verification, but add three points to group verification:

  1. When the groups are not assigned, authentication is required every time by default
  2. Multiple verifications can be performed on the same variable through groups grouping, as shown in the following code
//对用户名进行两次不同情况的验证。
@NotEmpty(groups={
    
    First.class})
@Size(min=1,max=10,groups={
    
    Second.class})
public String username; 
  1. By default, different grouping constraints are verified out of order, but in some cases the mutual constraints of verification are very important (for example, the previous group verification failed, the following will not be verified, etc.), so the verification of groups grouping also has Verification order before and after. Use the @GroupSequence annotation to sort.
/*
 * 分组顺序接口类
 */
import javax.validation.GroupSequence;

//分组序列先Frist再Second
@GroupSequence({
    
    First.class,Second.class})
public interface Group{
    
    

}

@Controller  
public class UserController {
    
      

    @RequestMapping("/saveAdd")  
    public String saveAddUser(@Validated({
    
    Group.class}) User user, BindingResult result) {
    
      
        if(result.hasErrors()) {
    
      
            return "error";  
        }  
        return "success";  
    }

3. Exception catch

We have learned how to perform data verification above, but there is a problem. If the verification fails, what should I do if an exception occurs? We want to get the exception information, and then tell the front end what exception happened.

How to encapsulate a JSON object and return it to the front end? Use a case to demonstrate

  1. Add test Bean, define several fields in the test class, and each field has certain restrictions
public class BeanValidation {
    
    
	@Size(min=6,max=10)
	private String field1;
	@Range(min=0,max=1)
	private Long field2;
	@AssertFalse
	private Boolean field3;
	public String getField1() {
    
    
		return field1;
	}
	public void setField1(String field1) {
    
    
		this.field1 = field1;
	}
	public Long getField2() {
    
    
		return field2;
	}
	public void setField2(Long field2) {
    
    
		this.field2 = field2;
	}
	public Boolean getField3() {
    
    
		return field3;
	}
	public void setField3(Boolean field3) {
    
    
		this.field3 = field3;
	}
	
}
  1. Add a test interface. The @Validated annotation is used in the interface to check the validity of the parameters. If the parameters are valid, return the original data
	@RequestMapping("globalexceptiontest")
	public Object globalExceptionTest(@Validated @RequestBody BeanValidation data)
	{
    
    
		ResultMsg resultMsg = new ResultMsg(ResultStatusCode.OK.getErrcode(), ResultStatusCode.OK.getErrmsg(), data);
		return resultMsg;
	}
  1. If the global exception handling is not added, the default exception handling will be used and the result will be returned, as shown below

Insert picture description hereThe returned result is very different from its own data structure, and it is also very troublesome for the front-end processing to return the result

  1. Data class returned by custom parameter exceptionArgumentInvalidResult
public class ArgumentInvalidResult {
    
    
	private String field;
	private Object rejectedValue;
	private String defaultMessage;
 
	public String getField() {
    
    
		return field;
	}
	public void setField(String field) {
    
    
		this.field = field;
	}
	public Object getRejectedValue() {
    
    
		return rejectedValue;
	}
	public void setRejectedValue(Object rejectedValue) {
    
    
		this.rejectedValue = rejectedValue;
	}
	public String getDefaultMessage() {
    
    
		return defaultMessage;
	}
	public void setDefaultMessage(String defaultMessage) {
    
    
		this.defaultMessage = defaultMessage;
	}
}
  1. Add global exception handling class GlobalExceptionHandler
@ControllerAdvice
//如果返回的为json数据或其它对象,添加该注解
@ResponseBody
public class GlobalExceptionHandler {
    
    
	//添加全局异常处理流程,根据需要设置需要处理的异常,本文以MethodArgumentNotValidException为例
	@ExceptionHandler(value=MethodArgumentNotValidException.class)
	public Object MethodArgumentNotValidHandler(HttpServletRequest request,
			MethodArgumentNotValidException exception) throws Exception
	{
    
    
		//按需重新封装需要返回的错误信息
		List<ArgumentInvalidResult> invalidArguments = new ArrayList<>();
		//解析原错误信息,封装后返回,此处返回非法的字段名称,原始值,错误信息
		for (FieldError error : exception.getBindingResult().getFieldErrors()) {
    
    
			ArgumentInvalidResult invalidArgument = new ArgumentInvalidResult();
			invalidArgument.setDefaultMessage(error.getDefaultMessage());
			invalidArgument.setField(error.getField());
			invalidArgument.setRejectedValue(error.getRejectedValue());
			invalidArguments.add(invalidArgument);
		}
		
		ResultMsg resultMsg = new ResultMsg(ResultStatusCode.PARAMETER_ERROR.getErrcode(), ResultStatusCode.PARAMETER_ERROR.getErrmsg(), invalidArguments);
		return resultMsg;
	}
}
  1. Run test
  • When the parameters are legal

Insert picture description here

  • When the parameter is illegal, return an error message with the same style as when the parameter is legal

Insert picture description here

note:

  1. Here @ControllerAdvice is annotated, @ControllerAdvice is an enhanced version of @Controller, generally used in conjunction with @ExceptionHandler.

If you mark @Controller, exception handling will only work on methods in the current controller class, but using @ControllerAdvice, it will be globally effective.

  1. Fill in the class object of the exception class you want to capture in the @ExceptionHandler annotation

Guess you like

Origin blog.csdn.net/saienenen/article/details/112756539
Recommended