最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot實現接口的各種參數校驗的示例

 更新時間:2022年01月26日 15:08:12   作者:ZNineSun  
本文主要介紹了SpringBoot實現接口的各種參數校驗的示例,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

在我們進行接口開發(fā)時,在對參數的接收時,我們需要冗余復雜的校驗規(guī)則 來進行接口參數的校驗,接下來推薦一個好用的校驗工具Spring Validation。

Java API規(guī)范 (JSR303) 定義了Bean校驗的標準validation-api,但沒有提供實現。hibernate validation是對這個規(guī)范的實現,并增加了校驗注解如@Email、@Length等。

Spring Validation是對hibernate validation的二次封裝,用于支持spring mvc參數自動校驗。接下來,我們以spring-boot項目為例,介紹Spring Validation的使用。

1.添加依賴

<!--新增validator-->
        <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
     <!--添加lombok-->
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

2.接口參數校驗

對于web服務來說,為防止非法參數對業(yè)務造成影響,在Controller層一定要做參數校驗的!大部分情況下,請求參數分為如下兩種形式:

  • POST、PUT請求,使用requestBody傳遞參數;
  • GET請求,使用requestParam/PathVariable傳遞參數。

下面我們簡單介紹下requestBody和requestParam/PathVariable的參數校驗實戰(zhàn)!

2.1 requestBody參數校驗

POST、PUT請求一般會使用requestBody傳遞參數,這種情況下,后端使用DTO對象進行接收。只要給 DTO 對象加上@Validated注解就能實現自動參數校驗。

比如,有一個保存User的接口,要求userName長度是2-10,account和password字段長度是6-20。如果校驗失敗,會拋出MethodArgumentNotValidException異常,Spring默認會將其轉為400(Bad Request)請求。

DTO 表示數據傳輸對象(Data Transfer Object),用于服務器和客戶端之間交互傳輸使用的。在 spring-web 項目中可以表示用于接收請求參數的Bean對象。

我們只需要兩步就可以實現剛剛對user對象參數的需求

1.在實體類上聲明約束注解

import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;

@Data
public class UserDTO {

    private Long userId;

    @NotNull
    @Length(min = 2, max = 10)
    private String userName;

    @NotNull
    @Length(min = 6, max = 20)
    private String account;

    @NotNull
    @Length(min = 6, max = 20)
    private String password;
}

2.在接口方法參數上聲明校驗注解

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class UserController {

    @PostMapping("/save")
    public Map<String, Object> saveUser(@RequestBody @Validated UserDTO userDTO) {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 20000);
        return result;
    }
}

接下來我們進行一下測試

在這里插入圖片描述

我們可以看到密碼位數不對時會轉為400請求參數不對的錯誤

2.2 requestParam/PathVariable參數校驗

GET請求一般會使用requestParam/PathVariable傳參。
如果參數比較多 (比如超過 6 個),還是推薦使用DTO對象接收。否則,推薦將一個個參數平鋪到方法入參中。在這種情況下,必須在Controller類上標注@Validated注解,并在入參上聲明約束注解 (如@Min等)。
如果校驗失敗,會拋出ConstraintViolationException異常。代碼示例如下:

package com.example.code.controller;

import com.example.code.po.UserDTO;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;

@RestController
public class UserController {
    @GetMapping("{userId}")
    public Map<String, Object> detail(@PathVariable("userId") @Min(10000000000000000L) Long userId) {
        Map<String, Object> result = new HashMap<>();
        UserDTO userDTO = new UserDTO();
        userDTO.setUserId(userId);
        userDTO.setAccount("11111111111111111");
        userDTO.setUserName("xixi");
        userDTO.setAccount("11111111111111111");
        result.put("code", 20000);
        result.put("data", userDTO);
        return result;
    }

    @GetMapping("getByAccount")
    public Map<String, Object> getByAccount(@Length(min = 6, max = 20) @NotNull String account) {
        Map<String, Object> result = new HashMap<>();
        UserDTO userDTO = new UserDTO();
        userDTO.setUserId(10000000000000003L);
        userDTO.setAccount(account);
        userDTO.setUserName("xixi");
        userDTO.setAccount(account);
        result.put("code", 20000);
        result.put("data", userDTO);
        return result;
    }
}

在這里插入圖片描述

3.統一異常處理

在我們剛剛的測試中會發(fā)現如果校驗失敗,會拋出MethodArgumentNotValidException或者ConstraintViolationException異常。
但是在實際項目開發(fā)中,通常會用統一異常處理來返回一個更友好的提示。比如我們系統要求無論發(fā)送什么異常,http的狀態(tài)碼必須返回200,由業(yè)務碼去區(qū)分系統的異常情況。

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.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class CommonExceptionHandler {

    @ExceptionHandler({MethodArgumentNotValidException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Map<String, Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        Map<String, Object> result = new HashMap<>();
        BindingResult bindingResult = ex.getBindingResult();
        StringBuilder sb = new StringBuilder("校驗失敗:");
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
        }
        String msg = sb.toString();
        result.put("code", 500);
        result.put("msg", msg);
        return result;
    }

    @ExceptionHandler({ConstraintViolationException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Map<String, Object> handleConstraintViolationException(ConstraintViolationException ex) {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 500);
        result.put("msg", ex.getMessage());
        return result;
    }
}

在這里插入圖片描述

4.進階使用

4.1 分組校驗

在實際項目中,可能多個方法需要使用同一個DTO類來接收參數,而不同方法的校驗規(guī)則很可能是不一樣的。
這個時候,簡單地在DTO類的字段上加約束注解無法解決這個問題。因此,spring-validation支持了分組校驗的功能,專門用來解決這類問題。
還是繼續(xù)使用上面的例子,比如保存User的時候,UserId是可空的,但是更新User的時候,UserId的值必須>=10000000000000000L;其它字段的校驗規(guī)則在兩種情況下一樣。這個時候使用分組校驗的代碼示例如下:

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

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

@Data
public class UserDTO_Groups {
    @Min(value = 10000000000000000L, groups = Update.class)
    private Long userId;

    @NotNull(groups = {Save.class, Update.class})
    @Length(min = 2, max = 10, groups = {Save.class, Update.class})
    private String userName;

    @NotNull(groups = {Save.class, Update.class})
    @Length(min = 6, max = 20, groups = {Save.class, Update.class})
    private String account;

    @NotNull(groups = {Save.class, Update.class})
    @Length(min = 6, max = 20, groups = {Save.class, Update.class})
    private String password;


    public interface Save {
    }

    public interface Update {
    }
}

在@Validated注解上指定校驗分組

import com.example.code.po.UserDTO;
import com.example.code.po.UserDTO_Groups;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;

@RestController
public class UserController {
    @PostMapping("/save_group")
    public Map<String, Object> saveUser_Group(@RequestBody @Validated(UserDTO_Groups.Save.class) UserDTO_Groups userDTO) {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 20000);
        result.put("data", userDTO);
        return result;
    }

    @PostMapping("/update_group")
    public Map<String, Object> updateUser(@RequestBody @Validated(UserDTO_Groups.Update.class) UserDTO_Groups userDTO) {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 20000);
        result.put("data", userDTO);
        return result;
    }
}

4.2 嵌套校驗

在實際開發(fā)中,我們保存User信息的時候同時還帶有Job信息。需要注意的是,此時DTO類的對應字段必須標記@Valid注解。

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

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

/**
 * 嵌套校驗
 */
@Data
public class UserDTO_Nest {
    @Min(value = 10000000000000000L, groups = Update.class)
    private Long userId;

    @NotNull(groups = {Save.class, Update.class})
    @Length(min = 2, max = 10, groups = {Save.class, Update.class})
    private String userName;

    @NotNull(groups = {Save.class, Update.class})
    @Length(min = 6, max = 20, groups = {Save.class, Update.class})
    private String account;

    @NotNull(groups = {Save.class, Update.class})
    @Length(min = 6, max = 20, groups = {Save.class, Update.class})
    private String password;

    @NotNull(groups = {Save.class, Update.class})
    @Valid
    private Job job;

    public interface Save {
    }

    public interface Update {
    }
}
import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

@Data
public class Job {

    @Min(value = 1, groups = UserDTO_Nest.Update.class)
    private Long jobId;

    @NotNull(groups = {UserDTO_Nest.Save.class, UserDTO_Nest.Update.class})
    @Length(min = 2, max = 10, groups = {UserDTO_Nest.Save.class, UserDTO_Nest.Update.class})
    private String jobName;

    @NotNull(groups = {UserDTO_Nest.Save.class, UserDTO_Nest.Update.class})
    @Length(min = 2, max = 10, groups = {UserDTO_Nest.Save.class, UserDTO_Nest.Update.class})
    private String position;

}

package com.example.code.controller;

import com.example.code.po.UserDTO;
import com.example.code.po.UserDTO_Groups;
import com.example.code.po.UserDTO_Nest;
import com.example.code.validate.ValidationList;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;

@RestController
public class UserController {

    @PostMapping("/save1")
    public Map<String, Object> save1(@RequestBody @Validated(UserDTO_Nest.Save.class) UserDTO_Nest userList) {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 20000);
        result.put("data", userList);
        return result;
    }
}

因為我們在代碼中設置了嵌套校驗要求job不能為空,所以:

在這里插入圖片描述

我們在job類匯總設置了position最小長度為2,所以:

在這里插入圖片描述

嵌套校驗可以結合分組校驗一起使用。
嵌套集合校驗會對集合里面的每一項都進行校驗,例如List< Job>字段會對這個list里面的每一個Job對象都進行校驗。

4.3 集合校驗

如果請求體直接傳遞了json數組給后臺,并希望對數組中的每一項都進行參數校驗。
此時,如果我們直接使用java.util.Collection下的list或者set來接收數據,參數校驗并不會生效!我們可以使用自定義list集合來接收參數:

1.包裝List類型,并聲明@Valid注解

package com.example.code.validate;

import lombok.experimental.Delegate;

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

public class ValidationList<E> implements List<E> {

    @Delegate
    @Valid
    public List<E> list = new ArrayList<>();

    @Override
    public String toString() {
        return list.toString();
    }
}

注意:@Delegate注解受lombok版本限制,1.18.6以上版本可支持。如果校驗不通過,會拋出NotReadablePropertyException,同樣可以使用統一異常進行處理。
接下來我們開發(fā)接口,使其可以批量保存

import com.example.code.po.UserDTO;
import com.example.code.po.UserDTO_Groups;
import com.example.code.po.UserDTO_Nest;
import com.example.code.validate.ValidationList;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;

@RestController
public class UserController {
    @PostMapping("/saveList")
    public Map<String, Object> saveList(@RequestBody @Validated(UserDTO_Nest.Save.class) ValidationList<UserDTO_Nest> userList) {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 20000);
        result.put("data", userList);
        return result;
    }
}

4.4 自定義校驗

在我們實際的業(yè)務開發(fā)中,我們的需求總是比框架提供的這些簡單校驗要復雜的多,我們可以通過自定義校驗來滿足我們的需求。

假設我們自定義加密id(由數字或者a-f的字母組成,32-256長度)校驗,主要分為兩步:

1.自定義約束注解

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {EncryptIdValidator.class})
public @interface EncryptId {

    String message() default "加密id格式錯誤";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

2.實現ConstraintValidator接口編寫約束校驗器

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class EncryptIdValidator implements ConstraintValidator<EncryptId, String> {

    private static final Pattern PATTERN = Pattern.compile("^[a-f\\d]{32,256}$");

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {

        if (value != null) {
            Matcher matcher = PATTERN.matcher(value);
            return matcher.find();
        }
        return true;
    }
}

3.在實體類上的字段中進行使用

import com.example.code.validate.EncryptId;
import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotNull;

@Data
public class UserDTO {

    private Long userId;

    @NotNull
    @Length(min = 2, max = 10)
    private String userName;

    @NotNull
    @Length(min = 6, max = 20)
    private String account;

    @NotNull
    @EncryptId
    private String password;
}

4.測試

import com.example.code.validate.ValidationList;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;

@RestController
public class UserController {
    @PostMapping("/save2")
    public Map<String, Object> save2(@RequestBody @Validated UserDTO userDTO) {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 20000);
        result.put("data", userDTO);
        return result;
    }
}

結果如下:

在這里插入圖片描述

我們可以看到自定義的校驗已經應用成功了

5.快速失敗 (Fail Fast)

Spring Validation默認會校驗完所有字段,然后才拋出異常。此時如果需要校驗字段過多則會影響前后端交互的速度,我們可以通過一些簡單的配置,開啟Fali Fast模式,一旦校驗失敗就立即返回。

import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

@Component
public class ValidatorConfig {
    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()

                .failFast(true)
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }
}

6.@Valid和@Validated區(qū)別

區(qū)別@Valid@Validated
提供者JSR-303 規(guī)范Spring
是否支持分組不支持支持
標注位置METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USETYPE, METHOD, PARAMETER
嵌套校驗支持不支持

7.實現原理

7.1 requestBody參數校驗實現原理

在spring-mvc中,RequestResponseBodyMethodProcessor是用于解析@RequestBody標注的參數以及處理@ResponseBody標注方法的返回值的。顯然,執(zhí)行參數校驗的邏輯肯定就在解析參數的方法resolveArgument()中:

于是我們看一下RequestResponseBodyMethodProcessor的resolveArgument()方法

在這里插入圖片描述

可以看到調用了validateIfApplicable()進行參數校驗,我們在看看validateIfApplicable()具體是如何校驗的

在這里插入圖片描述

我們可以看到真正的校驗,無論是@Validated、@Valid都是通過binder.validate(validationHints);實現,所以我們在接著往下看看WebDataBinder.validate()實現

在這里插入圖片描述

最終發(fā)現底層最終還是調用了Hibernate Validator進行真正的校驗處理

7.2 方法級別的參數校驗實現原理

上面提到的將參數一個個平鋪到方法參數中,然后在每個參數前面聲明約束注解的校驗方式,就是方法級別的參數校驗,如下圖所示:

在這里插入圖片描述

實際上,這種方式可用于任何Spring Bean的方法上,比如Controller/Service等。其底層實現原理就是AOP,具體來說是通過MethodValidationPostProcessor動態(tài)注冊AOP切面,然后使用MethodValidationInterceptor對切點方法織入增強。

我們看一下MethodValidationPostProcessor中源碼的實現

在這里插入圖片描述

最終調用的是MethodValidationInterceptor(),那么我們進入到該類看一下

在這里插入圖片描述

實際上,不管是requestBody參數校驗還是方法級別的校驗,最終都是調用Hibernate Validator執(zhí)行校驗,Spring Validation只是做了一層封裝。
至此對于接口參數的校驗就到此結束了,有問題歡迎大家來下方討論,謝謝。

到此這篇關于SpringBoot實現接口的各種參數校驗的示例的文章就介紹到這了,更多相關SpringBoot 接口參數校驗內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java使用poi組件導出Excel格式數據

    Java使用poi組件導出Excel格式數據

    這篇文章主要介紹了Java使用poi組件導出Excel格式數據,需要的朋友可以參考下
    2020-02-02
  • Java集合框架之List ArrayList LinkedList使用詳解刨析

    Java集合框架之List ArrayList LinkedList使用詳解刨析

    早在 Java 2 中之前,Java 就提供了特設類。比如:Dictionary, Vector, Stack, 和 Properties 這些類用來存儲和操作對象組。雖然這些類都非常有用,但是它們缺少一個核心的,統一的主題。由于這個原因,使用 Vector 類的方式和使用 Properties 類的方式有著很大不同
    2021-10-10
  • Spring Boot+maven打war包的方法

    Spring Boot+maven打war包的方法

    這篇文章主要介紹了Spring Boot+maven打war包的方法,本文通過實例代碼相結合的形式給大家介紹的非常詳細,需要的朋友參考下吧
    2018-05-05
  • jstl標簽基礎開發(fā)步驟(詳解)

    jstl標簽基礎開發(fā)步驟(詳解)

    下面小編就為大家?guī)硪黄猨stl標簽基礎開發(fā)步驟(詳解)。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • java中符號@的作用及用法舉例詳解

    java中符號@的作用及用法舉例詳解

    在Java編程語言中,@符號被稱為注解(Annotation),下面這篇文章主要介紹了java中符號@的作用及用法的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-04-04
  • 使用SpringBoot編寫一個優(yōu)雅的單元測試

    使用SpringBoot編寫一個優(yōu)雅的單元測試

    這篇文章主要為大家詳細介紹了如何使用SpringBoot編寫一個優(yōu)雅的單元測試,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-07-07
  • SpringBoot后端接收數組對象的實現

    SpringBoot后端接收數組對象的實現

    這篇文章主要介紹了SpringBoot后端接收數組對象的實現方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Java接口的作用_動力節(jié)點Java學院整理

    Java接口的作用_動力節(jié)點Java學院整理

    這篇文章主要介紹了Java接口的作用,涉及到接口的規(guī)范相關知識,需要的的朋友參考下
    2017-04-04
  • 解決Beanutils.copyproperties實體類對象不一致的問題

    解決Beanutils.copyproperties實體類對象不一致的問題

    這篇文章主要介紹了解決Beanutils.copyproperties實體類對象不一致的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • idea之Recompile、Rebuild和Build之間的區(qū)別及說明

    idea之Recompile、Rebuild和Build之間的區(qū)別及說明

    這篇文章主要介紹了idea之Recompile、Rebuild和Build之間的區(qū)別及說明,具有很好的參考價值,希望對有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08

最新評論

海口市| 高台县| 湖北省| 兴业县| 原阳县| 临湘市| 望江县| 普兰店市| 文化| 梨树县| 湖口县| 五河县| 平和县| 修水县| 游戏| 佛学| 望江县| 茂名市| 临潭县| 长泰县| 明水县| 安仁县| 金川县| 安顺市| 平泉县| 万州区| 清流县| 集安市| 长汀县| 商河县| 晴隆县| 永川市| 木兰县| 桦南县| 开封市| 莱西市| 碌曲县| 杂多县| 共和县| 仪陇县| 河北区|