欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

@Valid和@Validated注解校驗以及異常處理方式

 更新時間:2024年11月04日 14:40:52   作者:YD_1989  
在Javaweb開發(fā)中,防止數(shù)據(jù)庫惡意攻擊是至關(guān)重要的,盡管前端校驗可以起到一定的篩選作用,但通過工具如postman直接對后端發(fā)起請求的情況仍然需要后端進行嚴格的數(shù)據(jù)校驗,Java生態(tài)下,@Valid注解配合SpringBoot提供了一個便捷高效的后端數(shù)據(jù)校驗方案

前言

在Javaweb的開發(fā)中,為了防止懂技術(shù)的人對數(shù)據(jù)庫的惡意攻擊,我們通常使用參數(shù)校驗對無效數(shù)據(jù)進行篩選,Java生態(tài)下的@valid注解配置SpringBoot的使用可以方便快速的完成對數(shù)據(jù)校驗的各種場景。

同時數(shù)據(jù)校驗分為前端校驗后端校驗。

可為何前端做完校驗之后,還要在后端進行校驗?

如果有人拿到了url地址,使用第三方測試工具比如postman就可以跳過前端頁面的參數(shù)檢驗,所以為了數(shù)據(jù)庫數(shù)據(jù)正確性,我們十分有必要對傳來的數(shù)據(jù)在后端進行第二次校驗

一、@Valid注解

1、源碼解析

通過源碼可以看出:

@Valid注解可以作用于:方法、屬性(包括枚舉中的常量)、構(gòu)造函數(shù)、方法的形參上。

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Valid {
}

關(guān)于注解源碼解析,可以參考如下鏈接:

Java如何自定義注解

2、所屬的包

import javax.validation.Valid;

3、參數(shù)校驗使用注解

(1)空校驗

注解應用
@Null用于基本類型上,限制只能為null
@NotNull用在基本類型上;不能為null,但可以為empty,沒有Size的約束
@NotEmpty用在集合類上面;不能為null,而且長度必須大于0
@NotBlank只能作用在String上,不能為null,而且調(diào)用trim()后,長度必須大于0

插播一條小內(nèi)容?。?!null和empty有何區(qū)別?

String a = new String
String b = ""
String c = null
  1. 此時a是分配了內(nèi)存空間,但值為空,是絕對的空,是一種有值(值存在為空而已)
  2. 此時b是分配了內(nèi)存空間,值為空字符串,是相對的空,是一種有值(值存在為空字串)
  3. 此時c是未分配內(nèi)存空間,無值,是一種無值(值不存在)

(2)Boolean校驗

注解應用
@AssertFalse限制必須為false
@AssertTrue限制必須為true

(3)長度校驗

注解應用
@Size(max,min)驗證對象(Array,Collection,Map,String)長度是否在給定的范圍之內(nèi)
@Length(min=, max=)驗證字符串長度是否在給定的范圍之內(nèi)

(4)日期校驗

注解應用
@Past限制必須是一個過去的日期,并且類型為java.util.Date
@Future限制必須是一個將來的日期,并且類型為java.util.Date
@Pattern(value)限制必須符合指定的正則表達式

(5)數(shù)值校驗

建議使用在Stirng,Integer類型,不建議使用在int類型上,因為表單值為“”時無法轉(zhuǎn)換為int,但可以轉(zhuǎn)換為Stirng為"",Integer為null。

注解應用
@Min(value)驗證 Number 和 String 對象必須為一個不小于指定值的數(shù)字
@Max(value)驗證 Number 和 String 對象必須為一個不大于指定值的數(shù)字
@DecimalMax(value)限制必須為一個不大于指定值的數(shù)字,小數(shù)存在精度
@DecimalMin(value)限制必須為一個不小于指定值的數(shù)字,小數(shù)存在精度
@Digits(integer,fraction)限制必須為一個小數(shù),且整數(shù)部分的位數(shù)不能超過integer,小數(shù)部分的位數(shù)不能超過fraction
@Digits驗證 Number 和 String 的構(gòu)成是否合法
@Range(max =3 , min =1 , message = " ")Checks whether the annotated value lies between (inclusive) the specified minimum and maximum

Max和Min是對你填的“數(shù)字”是否大于或小于指定值,這個“數(shù)字”可以是number或者string類型。長度限制用length。

(6)其他校驗

注解應用
@Email驗證注解的元素值是Email,也可以通過正則表達式和flag指定自定義的email

4、具體使用

使用 @Valid 進行參數(shù)效驗步驟:

  1. 實體類中添加 @Valid 相關(guān)注解
  2. 接口類中添加 @Valid 注解
  3. 全局異常處理類中處理 @Valid 拋出的異常

運行流程:

整個過程如下圖所示,用戶訪問接口,然后進行參數(shù)效驗,因為 @Valid 不支持平面的參數(shù)效驗(直接寫在參數(shù)中字段的效驗)所以基于 GET 請求的參數(shù)還是按照原先方式進行效驗,而 POST 則可以以實體對象為參數(shù),可以使用 @Valid 方式進行效驗。

如果效驗通過,則進入業(yè)務邏輯,否則拋出異常,交由全局異常處理器進行處理。

代碼實踐:

(1)添加maven依賴(三種方式添加依賴)

			<!--第一種:valid依賴-->
		<dependency>
			<groupId>javax.validation</groupId>
			<artifactId>validation-api</artifactId>
			<version>版本號</version>
		</dependency>

		<!--		第二種:集成于web依賴中(注意版本號)-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<version>2.0.5.RELEASE</version>
		</dependency>

		<!--		第三種:springboot的validation-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>

(2)創(chuàng)建request實體類

import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;

@Data
@NoArgsConstructor
public class TestRequest {

    @NotBlank(message = "name不為空")
    private String name;

    @Length(max = 3,message = "address最大長度是3")
    private String address;

    @Max(value = 5,message = "reqNo最大值是5")
    private String reqNo;

}

(3)創(chuàng)建controller

@RestController
public class ValidTestController {

    @RequestMapping("/valid/test")
    public void test(@Valid @RequestBody TestRequest request){
        System.out.println(request);
    }

(4)postman測試

postman返回結(jié)果:

{
    "timestamp": "2022-11-12T09:54:24.202+00:00",
    "status": 400,
    "error": "Bad Request",
    "trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public void com.example.controller.ValidTestController.test(com.example.domain.TestRequest) with 3 errors: [Field error in object 'testRequest' on field 'name': rejected value []; codes [NotBlank.testRequest.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [testRequest.name,name]; arguments []; default message [name]]; default message [name不為空]] [Field error in object 'testRequest' on field 'reqNo': rejected value [8]; codes [Max.testRequest.reqNo,Max.reqNo,Max.java.lang.String,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [testRequest.reqNo,reqNo]; arguments []; default message [reqNo],5]; default message [reqNo最大值是5]] [Field error in object 'testRequest' on field 'address': rejected value [gtyjh]; codes [Length.testRequest.address,Length.address,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [testRequest.address,address]; arguments []; default message [address],3,0]; default message [address最大長度是3]] \r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:141)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:179)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:146)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:681)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:764)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:748)\r\n",
    "message": "Validation failed for object='testRequest'. Error count: 3",
    "errors": [
        {
            "codes": [
                "NotBlank.testRequest.name",
                "NotBlank.name",
                "NotBlank.java.lang.String",
                "NotBlank"
            ],
            "arguments": [
                {
                    "codes": [
                        "testRequest.name",
                        "name"
                    ],
                    "arguments": null,
                    "defaultMessage": "name",
                    "code": "name"
                }
            ],
            "defaultMessage": "name不為空",
            "objectName": "testRequest",
            "field": "name",
            "rejectedValue": "",
            "bindingFailure": false,
            "code": "NotBlank"
        },
        {
            "codes": [
                "Max.testRequest.reqNo",
                "Max.reqNo",
                "Max.java.lang.String",
                "Max"
            ],
            "arguments": [
                {
                    "codes": [
                        "testRequest.reqNo",
                        "reqNo"
                    ],
                    "arguments": null,
                    "defaultMessage": "reqNo",
                    "code": "reqNo"
                },
                5
            ],
            "defaultMessage": "reqNo最大值是5",
            "objectName": "testRequest",
            "field": "reqNo",
            "rejectedValue": "8",
            "bindingFailure": false,
            "code": "Max"
        },
        {
            "codes": [
                "Length.testRequest.address",
                "Length.address",
                "Length.java.lang.String",
                "Length"
            ],
            "arguments": [
                {
                    "codes": [
                        "testRequest.address",
                        "address"
                    ],
                    "arguments": null,
                    "defaultMessage": "address",
                    "code": "address"
                },
                3,
                0
            ],
            "defaultMessage": "address最大長度是3",
            "objectName": "testRequest",
            "field": "address",
            "rejectedValue": "gtyjh",
            "bindingFailure": false,
            "code": "Length"
        }
    ],
    "path": "/valid/test"
}

從后端返回給postman的結(jié)果可以看出,三個字段的校驗都已經(jīng)實現(xiàn)。但是,特別情況,RequestBody可能是嵌套的實體,這個時候,對于嵌套的實體類來說,嵌套必須加 @Valid,如果只在字段上添加校驗注解嵌套中的驗證不生效。

如果只在嵌套類字段上加上校驗注解,如下:

import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;

@Data
@NoArgsConstructor
public class TestRequest {

    @NotBlank(message = "name不為空")
    private String name;

    @Length(max = 3, message = "address最大長度是3")
    private String address;

    @Max(value = 5, message = "reqNo最大值是5")
    private String reqNo;

    private TestRequestInner inner;

    @Data
    @NoArgsConstructor
    public static class TestRequestInner {
        @Length(max = 3, message = "最大長度是3")
        private String sonName;

        private Integer sonAge;

        private String schoolNo;
    }
}

postman測試:

控制臺打?。?/p>

可以看出,嵌套類中sonName的長度校驗并沒有起到作用。

在嵌套類的外層加上@Valid注解,如下:

@Data
@NoArgsConstructor
public class TestRequest {

    @NotBlank(message = "name不為空")
    private String name;

    @Length(max = 3, message = "address最大長度是3")
    private String address;

    @Max(value = 5, message = "reqNo最大值是5")
    private String reqNo;

    @Valid
    private TestRequestInner inner;
    
//   即使放在list集合里面仍然是需要加上 @Valid 注解
//    @Valid
//    private List<TestRequestInner> inner;

    @Data
    @NoArgsConstructor
    public static class TestRequestInner {
        @Length(max = 3, message = "最大長度是3")
        private String sonName;

        private Integer sonAge;

        private String schoolNo;
    }
}

postman測試:

校驗成功。

5、異常處理

剛才的測試我們看到,校驗注解全部生效,但是所有的異常全部拋出給postman,從控制臺可以看出:

程序拋出了MethodArgumentNotValidException異常信息,在實際業(yè)務中,有時候需要處理這個異常,這個時候就需要一個全局異常處理類中處理 @Valid 拋出的異常。

拋出的異常結(jié)構(gòu):

代碼如下:

實體類如上不變,controller接口方法改為返回string:

 @RequestMapping("/valid/test")
    public String test(@Valid @RequestBody TestRequest request){
        System.out.println(request);
        return "success";
    }

異常處理類:

我們可以根據(jù)上圖拋出的異常結(jié)構(gòu),去get我們想要獲得的內(nèi)容。

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
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.List;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    /**
     * 自定義驗證異常
     * MethodArgumentNotValidException 方法參數(shù)無效異常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST) //設置狀態(tài)碼為 400
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public String paramExceptionHandler(MethodArgumentNotValidException e) {
        BindingResult exceptions = e.getBindingResult();
// 判斷異常中是否有錯誤信息,如果存在就使用異常中的消息,否則使用默認消息
        if (exceptions.hasErrors()) {
            List errors = exceptions.getAllErrors();
            if (!errors.isEmpty()) {
// 這里列出了全部錯誤參數(shù),按正常邏輯,只需要第一條錯誤即可
                FieldError fieldError = (FieldError) errors.get(0);
                return fieldError.getDefaultMessage();
            }
        }
        return "請求參數(shù)錯誤";
    }
}

postman測試:

6、springboot項目中的異常處理

上述的異常處理只是一個簡單的string返回,但是在實際項目中,返回結(jié)構(gòu)是固定的,下面對于固定的返回結(jié)構(gòu),做異常處理。

(1)request實體類

import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;

import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import java.util.List;

@Data
@NoArgsConstructor
public class TestRequest {

    @NotBlank(message = "name不為空")
    private String name;

    @Length(max = 3, message = "address最大長度是3")
    private String address;

    @Max(value = 5, message = "reqNo最大值是5")
    private String reqNo;

    @Valid
    private List<TestRequestInner> inner;

    @Data
    @NoArgsConstructor
    public static class TestRequestInner {
        @Length(max = 3, message = "最大長度是3")
        private String sonName;

        private Integer sonAge;

        @NotBlank(message = "schoolNo不空")
        private String schoolNo;
    }
}

(2)結(jié)果返回實體類

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

@Data
@NoArgsConstructor
@Builder
@AllArgsConstructor
public class ResponseResult {

    private List<ProvideInfo> provideInfos;

    @Data
    @NoArgsConstructor
    @Builder
    @AllArgsConstructor
    public static class ProvideInfo {
        private String code;
        private String detail;
    }
}

(3)controller接口方法

import com.example.domain.ResponseResult;
import com.example.domain.TestRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
public class ValidTestController {

    @RequestMapping("/valid/test")
    public ResponseEntity<ResponseResult> test(@Valid @RequestBody TestRequest request) {
        System.out.println(request);
        return null;
    }
}

(4)postman測試

再插播一個小插曲?。。?!

剛開始寫的結(jié)果返回實體類中的provideInfo是這樣式兒滴:

然后運行項目就出了這個錯:

英文版是這樣式兒滴:

上網(wǎng)查了一下,問題出現(xiàn)在這:

原因:

本應當(只能)使用無參構(gòu)造器,但編譯器卻發(fā)現(xiàn)代碼中使用了有參(全參)構(gòu)造器,這個全參構(gòu)造器出現(xiàn)在Builder類的build()方法中,該方法試圖調(diào)用一個全參構(gòu)造器。

這個報錯信息表明,@NoArgsConstructor抑制了@Builder生成全參構(gòu)造器,只生成了一個無參構(gòu)造器,使用lombok插件delombok @Builder@NoArgsConstructor兩個注解,可以證實這一抑制現(xiàn)象。

解決方法:

一種方法是同時使用@Builder、@NoArgsConstructor@AllArgConstructor,還有一種方法是顯式添加@Tolerate注解的無參構(gòu)造器。

然后在provideInfo上添加了@AllArgConstructor注解(如下圖),成功運行!

(5)全局異常處理類各種形式

對于全局異常類的處理,涉及到攔截器相關(guān)內(nèi)容,這里不做多說。全局異常類的處理方式有很多種:

方式一:

// Enum枚舉類
public enum CodeEnum {
// 根據(jù)自己的項目需求更改狀態(tài)碼,這里只是一個示范
    UNKNOW_EXCEPTION(10000,"系統(tǒng)未知錯誤"),
    VALID_EXCETIPON(10001,"參數(shù)格式校驗錯誤");
    private int code;
    private String msg;
    CodeEnum(int code, String msg){
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}


//異常處理類
@Slf4j
@RestControllerAdvice("com.cbj.db_work.controller") //表明需要處理異常的范圍
public class GlobalExceptionHandler {

    @ExceptionHandler(value = MethodArgumentNotValidException.class) // 參數(shù)異常拋出的異常類型為MethodArgumentNotValidException,這里捕獲這個異常
    // R為統(tǒng)一返回的處理類
    public R validExceptionHandler(MethodArgumentNotValidException e){
        System.out.println("數(shù)據(jù)異常處理");
        log.error("數(shù)據(jù)校驗出現(xiàn)問題,異常類型:{}",e.getMessage(),e.getClass());
        BindingResult bindingResult = e.getBindingResult();
        Map<String,String> map = new HashMap<>();
        bindingResult.getFieldErrors().forEach((item)->{
            String message = item.getDefaultMessage();
            // 獲取錯誤的屬性字段名
            String field = item.getField();
            map.put(field,message);
        });
        return R.error().code(CodeEnum.VALID_EXCETIPON.getCode()).message(CodeEnum.VALID_EXCETIPON.getMsg()).data("errorData",map);
    }
    
}

方式二:

@ControllerAdvice
@RestControllerAdvice
@Slf4j
public class ValidExceptionHandler extends GlobalExceptionHandler {

    // GET請求參數(shù)異常處理
    @ExceptionHandler(value = ConstraintViolationException.class)
    public Result<Object> constraintViolationExceptionHandler(ConstraintViolationException e) {
        StringBuilder msg = new StringBuilder();
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        for (ConstraintViolation<?> constraintViolation : constraintViolations) {
            String message = constraintViolation.getMessage();
            msg.append(message).append(";");
        }
        return ResultResponse.getFailResult(ResultCode.BODY_NOT_MATCH.getResultCode(), msg.toString());
    }

    @ExceptionHandler(ArithmeticException.class)
    public Result<Object> arithmeticExceptionHandler(ArithmeticException e) {
        e.printStackTrace();
        return ResultResponse.getFailResult(ResultCode.NOT_FOUND.getResultCode(), "算術(shù)異常!"+e.getMessage());
    }

    // POST請求參數(shù)異常處理
    @ExceptionHandler(BindException.class)
    public Result<Object> bindExceptionHandler(BindException e) {
        FieldError fieldError = e.getBindingResult().getFieldError();
        String msg;
        if (Objects.isNull(fieldError)) {
            msg = "POST請求參數(shù)異常:" + JSON.toJSONString(e.getBindingResult());
            log.info(msg);
        } else {
            msg = fieldError.getDefaultMessage();
        }
        return ResultResponse.getFailResult(ResultCode.BODY_NOT_MATCH.getResultCode(), msg);
    }

}

特別的,get請求校驗:

@RestController
@RequestMapping(value = "/test")
@Slf4j
//@ApiIgnore
@Validated
public class TestController {

    @GetMapping(value = "/test")
    public Result<Object> test(@NotNull(message = "name必傳")
                                   @NotBlank(message = "name格式錯誤")String name) {
        return ResultResponse.getSuccessResult("hello: " + name);
    }
}

二、@Validated注解

1、@Validated 和 @Valid 區(qū)別

  1. @Validate 是對@Valid 的封裝
  2. @Validate 可以進行分組驗證 ,一個對象中都寫了驗證而你只需要驗證該方法需要的驗證時使用

2、為何要分組校驗?

假設有這樣一種場景:

我們使用同一個VO(Request)類來傳遞save和update方法的數(shù)據(jù),但對于id來說,通常有框架幫我們生成id,我們不需要傳id此時需要使用注解@Null表名id數(shù)據(jù)必須為空。

但對于update方法,我們必須傳id才能進行update操作,所以同一個字段面對不同的場景不同需求就可以使用分組校驗。

3、代碼實操

(1)創(chuàng)建分組接口

這里并不需要實現(xiàn)編寫什么代碼,標明分類。

//分組接口 1
public interface InsertGroup {
}

//分組接口 2
public class UpdateGroup {
}

(2)Request實體類

@Data
@NoArgsConstructor
public class TestRequest {

    @Null(message = "無需傳id",groups = InsertGroup.class)
    @NotBlank(message = "必須傳入id",groups = UpdateGroup.class)
    private String id;
    
  }

(3)controller接口

	@RequestMapping("/valid/test")
    public ResponseEntity<ResponseResult> test(@Validated({UpdateGroup.class})@RequestBody TestRequest request) {
        System.out.println(request);
        return null;
    }

(4)postman測試

(5)注意事項!?。?/p>

當我們在controller層指定分組后,屬性上沒有表名分組的校驗還執(zhí)行么?

下面的Request實體中,id進行了分組,address沒有進行分組:

@Data
@NoArgsConstructor
public class TestRequest {

    @Null(message = "無需傳id",groups = InsertGroup.class)
    @NotBlank(message = "必須傳入id",groups = UpdateGroup.class)
    private String id;

	@Length(max = 3, message = "address最大長度是3")
    private String address;
    
  }

postman測試:

如下圖,校驗沒有成功?。。?!說明一旦開啟了分組校驗,就必須把所有的校驗規(guī)則都指定組別,不然不生效。

三、自定義校驗注解

業(yè)務場景

假設我們有一個字段比如showStatus只能由0和1兩個取值,我們可以使用正則,也可以自定義注解校驗,這里我們展示如何使用自定義校驗.

如下圖所示:

自定義注解實現(xiàn)過程

(1)編寫自定義注解

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

// Target表示注解使用的范圍
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
// 獲取注解的時間,固定值
@Retention(RetentionPolicy.RUNTIME)
// 匹配的校驗器,我們稍后編寫,關(guān)聯(lián)注解和校驗器
@Constraint(validatedBy = {StatusValueValidator.class})
@Documented
public @interface StatusValue {

    // 錯誤信息去哪找,通常我們使用配置文件,稍后編寫
    String message() default "{com.cbj.db_work.valid.ListValue.message}";
    // 支持分組校驗
    Class<?>[] groups() default {};
    // 自定義負載信息
    Class<? extends Payload>[] payload() default {};
    // 指定參數(shù),就是上圖中指定的可取值的范圍
    int []  vals() default {};
}

(2)自定義校驗器

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

// 實現(xiàn)ConstraintValidator接口,泛型值:<自定義注解類,被校驗值的數(shù)據(jù)類型>
public class StatusValueValidator implements ConstraintValidator<StatusValue, Integer> {

    // 整體思路,使用set在initialize獲得參數(shù)信息,在isValid方法中校驗,成功true,失敗false
    Set<Integer> set = new HashSet<>();

    // 初始化方法,可以得到詳細信息
    @Override
    public void initialize(StatusValue constraintAnnotation) {
        int[] vals = constraintAnnotation.vals();
        for (int val : vals) {
            set.add(val);
        }
    }

/**
     * 校驗是否匹配
     * @param value 就是需要校驗的值
     * @param constraintValidatorContext
     * @return
     */
    @Override
    public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
        return set.contains(integer);
    }
}

(3)編寫配置文件

com.cbj.db_work.valid.ListValue.message=必須提交指定的值

在配置文件中可以指定匹配錯誤時顯示的信息,也可以message指定。

(4)postman測試

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • java算法題解LeetCode35復雜鏈表的復制實例

    java算法題解LeetCode35復雜鏈表的復制實例

    這篇文章主要為大家介紹了java算法題解LeetCode35復雜鏈表的復制實例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • IDEA?Error:java:無效的源發(fā)行版:13的解決過程

    IDEA?Error:java:無效的源發(fā)行版:13的解決過程

    之前用idea運行時,也會出現(xiàn)這種情況,后面通過網(wǎng)上的資料解決了這個問題,下面這篇文章主要給大家介紹了關(guān)于IDEA?Error:java:無效的源發(fā)行版:13的解決過程,需要的朋友可以參考下
    2023-01-01
  • Java8新特性Stream流實例詳解

    Java8新特性Stream流實例詳解

    Stream流是數(shù)據(jù)渠道,用于操作數(shù)據(jù)源(集合、數(shù)組等)所生成的元素序列。這篇文章主要介紹了Java8新特性Stream流的相關(guān)資料,需要的朋友參考下吧
    2017-10-10
  • 關(guān)于@Component注解的含義說明

    關(guān)于@Component注解的含義說明

    這篇文章主要介紹了關(guān)于@Component注解的含義說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java實戰(zhàn)之基于TCP實現(xiàn)簡單聊天程序

    Java實戰(zhàn)之基于TCP實現(xiàn)簡單聊天程序

    這篇文章主要為大家詳細介紹了如何在Java中基于TCP實現(xiàn)簡單聊天程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Springboot整合easyexcel實現(xiàn)一個接口任意表的Excel導入導出

    Springboot整合easyexcel實現(xiàn)一個接口任意表的Excel導入導出

    本文主要介紹了Springboot整合easyexcel實現(xiàn)一個接口任意表的Excel導入導出,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-02-02
  • 詳解Java如何優(yōu)雅的使用裝飾器模式

    詳解Java如何優(yōu)雅的使用裝飾器模式

    裝飾器設計模式大家肯定都聽說過,但是有沒有使用過呢,今天本君就跟大家分享一下裝飾器模式應該如何使用,感興趣的小伙伴可以學習一下
    2022-09-09
  • Java System類詳解_動力節(jié)點Java學院整理

    Java System類詳解_動力節(jié)點Java學院整理

    System類是jdk提供的一個工具類,有final修飾,不可繼承,由名字可以看出來,其中的操作多數(shù)和系統(tǒng)相關(guān)。這篇文章主要介紹了Java System類詳解_動力節(jié)點Java學院整理,需要的朋友可以參考下
    2017-04-04
  • java 分行讀取實例

    java 分行讀取實例

    今天小編就為大家分享一篇java 分行讀取實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • Java行為型模式中命令模式分析

    Java行為型模式中命令模式分析

    在軟件設計中,我們經(jīng)常需要向某些對象發(fā)送請求,但是并不知道請求的接收者是誰,也不知道被請求的操作是哪個,我們只需在程序運行時指定具體的請求接收者即可,此時可以使用命令模式來進行設計
    2023-02-02

最新評論