Spring Boot 2 中的參數(shù)校驗(yàn) spring-boot-starter-validation/Hibernate Validator

Spring Boot 2 中的參數(shù)校驗(yàn) spring-boot-starter-validation/Hibernate Validator

Validation in Spring Boot

在springboot中常用的用于參數(shù)校驗(yàn)的注解如下:

@AssertFalse 所注解的元素必須是Boolean類型,且值為false
@AssertTrue 所注解的元素必須是Boolean類型,且值為true
@DecimalMax 所注解的元素必須是數(shù)字,且值小于等于給定的值
@DecimalMin 所注解的元素必須是數(shù)字,且值大于等于給定的值
@Digits 所注解的元素必須是數(shù)字,且值必須是指定的位數(shù)
@Future 所注解的元素必須是將來某個(gè)日期
@Max 所注解的元素必須是數(shù)字,且值小于等于給定的值
@Min 所注解的元素必須是數(shù)字,且值小于等于給定的值
@Range 所注解的元素需在指定范圍區(qū)間內(nèi)
@NotNull 所注解的元素值不能為null
@NotBlank 所注解的元素值有內(nèi)容
@Null 所注解的元素值為null
@Past 所注解的元素必須是某個(gè)過去的日期
@PastOrPresent 所注解的元素必須是過去某個(gè)或現(xiàn)在日期
@Pattern 所注解的元素必須滿足給定的正則表達(dá)式
@Size 所注解的元素必須是String、集合或數(shù)組,且長度大小需保證在給定范圍之內(nèi)
@Email 所注解的元素需滿足Email格式

一、添加依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

這個(gè)starter依賴的是Hibernate Validator。

二、實(shí)體類參數(shù)校驗(yàn)

(一)實(shí)體類上加上注解

import lombok.Data;

import javax.validation.constraints.*;
import java.io.Serializable;

/**
 * @author chushiyan
 * @email  Y2h1c2hpeWFuMDQxNUAxNjMuY29t(base64)
 * @description
 */
@Data
public class User implements Serializable {

    private String id;

    @NotNull(message = "姓名不能為空")
    @Size(min = 1, max = 20, message = "姓名長度必須在1-20之間")
    private String name;

    @Min(value = 10, message = "年齡必須大于10")
    @Max(value = 150, message = "年齡必須小于150")
    private Integer age;

    @Email(message = "郵箱格式不正確")
    private String email;
}

(二)Controller中加上注解

在controller中使用@Valid 或者@Validated 注解校驗(yàn)

import com.chushiyan.validation_tutorial.entity.Result;
import com.chushiyan.validation_tutorial.pojo.User;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

/**
 * @author chushiyan
 * @email  Y2h1c2hpeWFuMDQxNUAxNjMuY29t(base64)
 * @description
 */
@RestController
@RequestMapping("/user")
public class UserController  {

    @PostMapping
    public Result test(@Valid  @RequestBody User user){
        System.out.println(user);
        return new Result(true,200,"");
    }
}

(三)測試

使用postman發(fā)送POST請(qǐng)求:http://localhost:10000/user

{
    "age":120,
    "email":"chushiyan"
}

控制臺(tái)打?。?/p>

WARN 12476 --- [io-10000-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.chushiyan.validation_tutorial.entity.Result com.chushiyan.validation_tutorial.controller.UserController.test(com.chushiyan.validation_tutorial.pojo.User) with 2 errors: [Field error in object 'user' on field 'name': rejected value [null]; codes [NotNull.user.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.name,name]; arguments []; default message [name]]; default message [姓名不能為空]] [Field error in object 'user' on field 'email': rejected value [chushiyan]; codes [Email.user.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@20195c7a,.*]; default message [郵箱格式不正確]] ]

響應(yīng)的數(shù)據(jù):

{
    "timestamp": "2019-12-03T09:14:00.759+0000",
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "NotNull.user.name",
                "NotNull.name",
                "NotNull.java.lang.String",
                "NotNull"
            ],
            "arguments": [
                {
                    "codes": [
                        "user.name",
                        "name"
                    ],
                    "arguments": null,
                    "defaultMessage": "name",
                    "code": "name"
                }
            ],
            "defaultMessage": "姓名不能為空",
            "objectName": "user",
            "field": "name",
            "rejectedValue": null,
            "bindingFailure": false,
            "code": "NotNull"
        },
        {
            "codes": [
                "Email.user.email",
                "Email.email",
                "Email.java.lang.String",
                "Email"
            ],
            "arguments": [
                {
                    "codes": [
                        "user.email",
                        "email"
                    ],
                    "arguments": null,
                    "defaultMessage": "email",
                    "code": "email"
                },
                [],
                {
                    "arguments": null,
                    "defaultMessage": ".*",
                    "codes": [
                        ".*"
                    ]
                }
            ],
            "defaultMessage": "郵箱格式不正確",
            "objectName": "user",
            "field": "email",
            "rejectedValue": "chushiyan",
            "bindingFailure": false,
            "code": "Email"
        }
    ],
    "message": "Validation failed for object='user'. Error count: 2",
    "trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.chushiyan.validation_tutorial.entity.Result com.chushiyan.validation_tutorial.controller.UserController.test(com.chushiyan.validation_tutorial.pojo.User) with 2 errors: [Field error in object 'user' on field 'name': rejected value [null]; codes [NotNull.user.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.name,name]; arguments []; default message [name]]; default message [姓名不能為空]] [Field error in object 'user' on field 'email': rejected value [chushiyan]; codes [Email.user.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@20195c7a,.*]; default message [郵箱格式不正確]] \r\n\tat  (博主進(jìn)行了省略......)",
    "path": "/user"
}

(四)全局處理異常

上面響應(yīng)的錯(cuò)誤肯定是不夠友好的,所以需要進(jìn)行異常處理。這里定義一個(gè)全局處理函數(shù)

import com.chushiyan.validation_tutorial.entity.Result;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author chushiyan
 * @email  Y2h1c2hpeWFuMDQxNUAxNjMuY29t(base64)
 * @description
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 處理所有校驗(yàn)失敗的異常(MethodArgumentNotValidException異常)
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    // 設(shè)置響應(yīng)狀態(tài)碼為400
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result handleBindGetException(MethodArgumentNotValidException ex) {

        Map<String, Object> body = new LinkedHashMap<String, Object>();
        body.put("timestamp", new Date());

        // 獲取所有異常
        List<String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(x -> x.getDefaultMessage())
                .collect(Collectors.toList());
        body.put("errors", errors);
        return new Result(false, 20001, "提交的數(shù)據(jù)校驗(yàn)失敗", body);
    }
}

(五)再次測試

使用postman發(fā)送POST請(qǐng)求:http://localhost:10000/user

{
    "age":120,
    "email":"chushiyan"
}

響應(yīng)的json數(shù)據(jù):

{
    "flag": false,
    "code": 20001,
    "message": "提交的數(shù)據(jù)校驗(yàn)失敗",
    "data": {
        "timestamp": "2019-12-03T09:35:02.815+0000",
        "errors": [
            "郵箱格式不正確",
            "姓名不能為空"
        ]
    }
}

三、單個(gè)參數(shù)校驗(yàn)

(一)直接在參數(shù)前加上校驗(yàn)注解:

package com.chushiyan.validation_tutorial.controller;

import com.chushiyan.validation_tutorial.entity.Result;
import com.chushiyan.validation_tutorial.pojo.User;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

/**
 * @author chushiyan
 * @email  Y2h1c2hpeWFuMDQxNUAxNjMuY29t(base64)
 * @description
 */
@RestController
@RequestMapping("/user")
@Validated
public class UserController  {

    @GetMapping
    public Result test2(@NotNull(message = "name不能為空")  String name){
        System.out.println(name);
        return new Result(true,200,"");
    }
}

注意:需要在類上添加@Validated注解,否則不會(huì)校驗(yàn)。

(二)全局處理函數(shù)

import com.chushiyan.validation_tutorial.entity.Result;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author chushiyan
 * @email  Y2h1c2hpeWFuMDQxNUAxNjMuY29t(base64)
 * @description
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 處理所有參數(shù)校驗(yàn)時(shí)拋出的異常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = ValidationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result handleBindException(ValidationException ex) {

        Map<String, Object> body = new LinkedHashMap<String, Object>();
        body.put("timestamp", new Date());

        // 獲取所有異常
        List<String> errors = new LinkedList<String>();
        if(ex instanceof ConstraintViolationException){
            ConstraintViolationException exs = (ConstraintViolationException) ex;
            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            for (ConstraintViolation<?> item : violations) {
                errors.add(item.getMessage());
            }
        }
        body.put("errors", errors);
        return new Result(true, 20001, "提交的參數(shù)校驗(yàn)失敗", body);
    }
}

(二)測試

postman 測試http://localhost:10000/user GET

{
    "flag": true,
    "code": 20001,
    "message": "提交的參數(shù)校驗(yàn)失敗",
    "data": {
        "timestamp": "2019-12-03T09:58:45.212+0000",
        "errors": [
            "name不能為空"
        ]
    }
}

四、參數(shù)校驗(yàn)分組

在實(shí)際開發(fā)中經(jīng)常會(huì)遇到這種情況:添加用戶時(shí),id是由后端生成的,不需要校驗(yàn)id是否為空,但是修改用戶時(shí)就需要校驗(yàn)id是否為空。如果在接收參數(shù)的User實(shí)體類的id屬性上添加NotNull,顯然無法實(shí)現(xiàn)。這時(shí)候就可以定義分組,在需要校驗(yàn)id的時(shí)候校驗(yàn),不需要的時(shí)候不校驗(yàn)。

(一)定義表示組別的接口類

package com.chushiyan.validation_tutorial.validate;
public interface GroupA {
}

(二)在實(shí)體類的注解中標(biāo)記id使用上面定義的組

給id屬性添加分組:

package com.chushiyan.validation_tutorial.pojo;

import com.chushiyan.validation_tutorial.validate.GroupA;
import lombok.Data;

import javax.validation.constraints.*;
import java.io.Serializable;

/**
 * @author chushiyan
 * @email  Y2h1c2hpeWFuMDQxNUAxNjMuY29t(base64)
 * @description
 */
@Data
public class User implements Serializable {

    @NotNull(groups = GroupA.class, message = "id不能為空")
    private String id;

    @NotNull(message = "姓名不能為空")
    @Size(min = 1, max = 20, message = "姓名長度必須在1-20之間")
    private String name;

    @Min(value = 10, message = "年齡必須大于10")
    @Max(value = 150, message = "年齡必須小于150")
    private Integer age;

    @Email(message = "郵箱格式不正確")
    private String email;
}

(三)在controller中使用@Validated指定使用哪個(gè)組

    @PostMapping
    public Result add(@Validated @RequestBody User user) {
        return new Result(true, 200, "增加用戶成功");
    }

    @PutMapping("/update")
    // 指定GroupA,這樣就會(huì)校驗(yàn)id屬性是否為空
    // 注意:還得必須添加Default.class,否則不會(huì)執(zhí)行其他的校驗(yàn)(如我們案例中的@Email)
    public Result update(@Validated({GroupA.class, Default.class}) @RequestBody User user) {
        return new Result(true, 200, "修改用戶成功");
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容