springboot-后端参数校验javax.validation

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

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

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区别

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZHOU_VIP

您的鼓励将是我创作最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值