springboot-后端参数校验javax.validation

  • Post author:
  • Post category:java


一般系统对表单的提交都会提供一定的校验,分为前台校验和后台校验,前台校验主要为了减轻服

务器的负担,后台校验增加系统安全性。


javax.validation

的一系列注解可以帮我们完成参数校验,免去繁琐的串行校验


什么是

javax.validation

JSR303 是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些

注解加在我们JavaBean的属性上面(面向注解编程的时代),就可以在需要校验的时候进行校验了,

在SpringBoot中已经包含在starter-web中,再其他项目中可以引用依赖,并自行调整版本。

步骤:

1. @Validated 声明要检查的参数

/***
 * 新增Member
 * @param member
 * @return
 */
@PostMapping("/save")
public ResponseEntity<Object> add(@RequestBody @Validated Member member){
    log.info("参数 : {}", member);
    memberService.add(member);
    return ResponseEntity.ok("success");
}

2. 对参数的字段进行注解标注

@NotBlank(message = "账号不能为空!")
private String account;

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

@Email(message = "邮箱格式不对")
private String mail;

@Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
private String mobile;

3. 在全局校验中增加校验异常


MethodArgumentNotValidException

是springBoot中进行绑定参数校验时的异常

/**
 * 方法参数校验
 */
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
    return ResponseEntity.accepted().body(ErrorVo.build("1000",e.getBindingResult().getFieldError().getDefaultMessage()));
}

全代码:

package com.nanjing.modules.resource.model;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;

import com.nanjing.common.Constants;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import lombok.experimental.Accessors;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;

/**
 * @program: backend
 * @since: 2021-11-12 16:36:58
 **/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("member")
@ApiModel(value="Member对象", description="成员")
public class Member implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "主键")
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    @NotBlank(message = "账号不能为空!")
    private String account;

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

    @ApiModelProperty(value = "部门代码")
    private String department;

    @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
    private String mobile;

    @Email(message = "邮箱格式不对")
    private String mail;

    @ApiModelProperty(value = "性别 M 男 F女")
    private String gender;

    private String birthYear;

    @ApiModelProperty(value = "Associate专科 Bachelor 本科 Master  硕士 Doctor  博士")
    private String educationBg;

    @ApiModelProperty(value = "工作经验(工龄)")
    private Integer experience;

    private Integer companyId;

    @JsonFormat(pattern= Constants.DATE_FORMAT, timezone = Constants.TIME_ZONE)
    private Date onBoardDate;

    @JsonFormat(pattern= Constants.DATE_FORMAT, timezone = Constants.TIME_ZONE)
    private Date offBoardDate;

    private String comment;

    @ApiModelProperty(value = "0代表在职,1代表离职")
    private Integer isTermination;


}
package com.nanjing.modules.member;

import com.nanjing.common.CommonResult;
import com.nanjing.modules.resource.model.Member;
import com.nanjing.modules.resource.service.MemberService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;

/**
 * @Date 2021/12/6
 */

@RestController
@RequestMapping("/member")
public class MemberController {

    private static final Logger log = LogManager.getLogger("base");

    @Autowired
    private MemberService memberService;

    /**
     * Member列表
     * @param page
     * @param size
     * @param member
     * @return
     */
    @PostMapping("/memberList/{page}/{size}")
    public ResponseEntity<Object> memberList(@PathVariable Integer page,
                                             @PathVariable Integer size,
                                             @RequestBody(required = false) Member member) {
        PageHelper.startPage(page,size);
        List<Member> memberList = memberService.queryMemberPage(member);
        PageInfo<Member> pageInfo = new PageInfo<>(memberList);
        return ResponseEntity.ok(CommonResult.map("pageResult", pageInfo));
    }




    /**
     * Member
     * @param id
     * @return
     */
    @GetMapping("/member/{id}")
    public ResponseEntity<Object> queryMemberById(@PathVariable String id) {
        Member member = memberService.queryMemberById(id);
        return ResponseEntity.ok(CommonResult.map("member", member));
    }


    /***
     * 新增Member
     * @param member
     * @return
     */
    @PostMapping("/save")
    public ResponseEntity<Object> add(@RequestBody @Validated Member member){
        log.info("参数 : {}", member);
        memberService.add(member);
        return ResponseEntity.ok("success");
    }


    /***
     * 更新Member
     * @param member
     * @return
     */
    @PutMapping(value="/update/{id}")
    public ResponseEntity<Object> update(@RequestBody Member member,@PathVariable Integer id) {
        member.setId(id);
        memberService.update(member);
        return ResponseEntity.ok("success");
    }


    /***
     * 根据ID删除
     * @param id
     * @return
     */
    @DeleteMapping(value = "/delete/{id}")
    public ResponseEntity<Object> delete(@PathVariable Integer id){
        memberService.delete(id);
        return ResponseEntity.ok("success");
    }





}
package com.nanjing.component;

import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.nanjing.component.ErrorCodeEnum.*;

import java.util.List;

/**
 * @program: backend
 * @description:
 * @since: 2021-11-18 15:54:55
 **/
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ResponseBody
    @ExceptionHandler(BaseException.class)
    public ResponseEntity<ErrorVo> handleBaseException(BaseException exception){
        return ResponseEntity.status(exception.getHttpCode())
                .body(ErrorVo.build(exception.getBizCode(), exception.getMessage()));
    }

    /**
     * 运行异常处理
     *
     * @param error 异常对象
     * @return 响应对象
     */
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<Object> handleException(RuntimeException error) {
        error.printStackTrace();
        return ResponseEntity.status(ErrorCodeEnum.SERVER_ERROR.getHttpCode())
                .body(ErrorVo.build(ErrorCodeEnum.SERVER_ERROR.getBizCode(), error.getMessage()));
    }

    /**
     * 其他系统异常处理
     *
     * @param error 异常对象
     * @return 响应对象
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Object> handleException(Exception error) {
        error.printStackTrace();
        return ResponseEntity.status(ErrorCodeEnum.SERVER_ERROR.getHttpCode())
                .body(ErrorVo.build(ErrorCodeEnum.SERVER_ERROR.getBizCode(), error.getMessage()));
    }


    /**
     * 方法参数校验
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        return ResponseEntity.accepted().body(ErrorVo.build("1000",e.getBindingResult().getFieldError().getDefaultMessage()));
    }


    /**
     * 校验异常
     *
     * @param ex MethodArgumentNotValidException
     * @return Response
     */
    /*@ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Response handleConstraintViolationException(MethodArgumentNotValidException ex) {
        //获取所有错误异常
        List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();
        //只返回第一个信息
        ObjectError error = allErrors.get(0);
        //返回自定义信息格式
        return ResponseHelper.fail(error.getDefaultMessage());
    }*/



}

postman测试:


拓展1:

自定义参数注解


优雅的校验参数-javax.validation – 简书


拓展2:

分组校验

有的时候,我们在某一个实体类中定义了很多校验规则,但是在某一次业务处理中,并不需要这么

多校验规则,此时就可以使用分组校验。具体步骤如下。

1,创建分组接口

首先创建两个分组接口:

1

2

3

4

5


public


interface


ValidationGroup1 {


}


public


interface


ValidationGroup2 {


}

2,在实体类中添加分组信息

这次在注解中添加了groups属性,表示该校验规则所属的分组:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24


@NoArgsConstructor


@Setter


@Getter


public


class


User  {




// @Size 注解表示一个字符串的长度或者一个集合的大小,必须在某一个范围中




@Size


(min =


5


, max =


10


, message =


"{user.name.size}"


, groups = ValidationGroup1.


class


)




private


String name;




// @NotEmpty 注解表示该字段不能为空




@NotEmpty


(message =


"{user.address.notnull}"


, groups = ValidationGroup2.


class


)




private


String address;




// @DecimalMin 注解表示对应属性值的下限




@DecimalMin


(value =


"1"


, message =


"{user.age.size}"


)




// @DecimalMax 注解表示对应属性值的上限




@DecimalMax


(value =


"200"


, message =


"{user.age.size}"


)




private


Integer age;




// @Email 注解表示对应属性格式是一个 Email




@Email


(message =


"{user.email.pattern}"


)




// @NotNull 注解表示该字段不能为null




@NotNull


(message =


"{user.email.notnull}"


, groups = ValidationGroup2.


class


)




private


String email;


}

3,在Controller中指定校验分组

接下来在@Validated注解中指定校验分组,这里的@Validated(ValidationGroup2.class) 表示这

里的校验使用ValidationGroup2分组的校验规则(只校验用户地址、邮箱地址是否为空):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18


@RestController


public


class


HelloController {




@PostMapping


(


"/user"


)




public


List<String> addUser(


@Validated


(ValidationGroup2.


class


) User user,




BindingResult result) {




List<String> errors =


new


ArrayList<>();




// 如果 BindingResult 的 hasErrors 方法返回true,则表示有错误信息




if


(result.hasErrors()) {




List<ObjectError> allErrors = result.getAllErrors();




/// 遍历错误信息,返回给前端




for


(ObjectError error : allErrors) {




errors.add(error.getDefaultMessage());




}




}




return


errors;




}


}


SpringBoot – 后端数据校验的实现(附样例)


@Null   被注释的元素必须为 null    
@NotNull    被注释的元素必须不为 null    
@AssertTrue     被注释的元素必须为 true    
@AssertFalse    被注释的元素必须为 false    
@Min(value)     被注释的元素必须是一个数字,其值必须大于等于指定的最小值    
@Max(value)     被注释的元素必须是一个数字,其值必须小于等于指定的最大值    
@DecimalMin(value)  被注释的元素必须是一个数字,其值必须大于等于指定的最小值    
@DecimalMax(value)  被注释的元素必须是一个数字,其值必须小于等于指定的最大值    
@Size(max=, min=)   被注释的元素的大小必须在指定的范围内    
@Digits (integer, fraction)     被注释的元素必须是一个数字,其值必须在可接受的范围内    
@Past   被注释的元素必须是一个过去的日期    
@Future     被注释的元素必须是一个将来的日期    
@Pattern(regex=,flag=)  被注释的元素必须符合指定的正则表达式    


Hibernate Validator提供的校验注解:  
@NotBlank(message =)   验证字符串非null,且trim后长度必须大于0    
@Email  被注释的元素必须是电子邮箱地址    
@Length(min=,max=)  被注释的字符串的大小必须在指定的范围内    
@NotEmpty   被注释的字符串的必须非空    
@Range(min=,max=,message=)  被注释的元素必须在合适的范围内

@AssertFalse 校验false  
@AssertTrue 校验true  
@DecimalMax(value=,inclusive=) 小于等于value,  
inclusive=true,是小于等于  
@DecimalMin(value=,inclusive=) 与上类似  
@Max(value=) 小于等于value  
@Min(value=) 大于等于value  
@NotNull  检查Null  
@Past  检查日期  
@Pattern(regex=,flag=)  正则  
@Size(min=, max=)  字符串,集合,map限制大小  
@Valid 对po实体类进行校验如需其他注解,请参考hibernate validator官方文档了解其他验证约束注解和进行自定义的验证约束注解定义。


javax.validation参数校验 – 景、 – 博客园


@NotEmpty、@NotBlank、@NotNull 区别和使用

@NotNull
适用于基本数据类型(Integer,Long,Double等等),当 @NotNull 注解被使用在 String 类型的数据上,则表示该数据不能为 Null(但是可以为Empty)
@NotBlank
适用于 String 类型的数据上,加了@NotBlank 注解的参数不能为 Null 且 trim() 之后 size > 0
@NotEmpty
适用于 String、Collection集合、Map、数组等等,加了@NotEmpty 注解的参数不能为 Null 或者 长度为 0


@NotEmpty、@NotBlank、@NotNull 区别和使用_yangchao1125的博客-CSDN博客_notempty和notblank区别



版权声明:本文为ZHOU_VIP原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。