91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java SpringBoot Validation怎么用

發布時間:2021-09-06 17:31:58 來源:億速云 閱讀:246 作者:小新 欄目:開發技術

這篇文章主要為大家展示了“Java SpringBoot Validation怎么用”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“Java SpringBoot Validation怎么用”這篇文章吧。

    提到輸入參數的基本驗證(非空、長度、大小、格式…),在以前我們還是通過手寫代碼,各種if、else、StringUtils.isEmpty、CollectionUtils.isEmpty…,真感覺快要瘋了,太繁瑣,Low爆了…,其實在Java生態提供了一套標準JSR-380(aka. Bean Validation 2.0,part of Jakarta EE and JavaSE),它已成為對象驗證事實上的標準,這套標準可以通過注解的形式(如@NotNull, @Size…)來對bean的屬性進行驗證。而Hibernate Validator對這套標準進行了實現,SpringBoot Validation無縫集成了Hibernate Validator、自定義驗證器、自動驗證的功能。下文將對SpringBoot集成Validation進行展開。

    constraints分類

    JSR-380的支持的constrants注解匯總如下表:

    分類注解適用對象null是否驗證通過說明
    非空@NotNull所有對象No不是null
    非空@NotEmptyCharSequence, Collection, Map, ArrayNo不是null、不是""、size>0
    非空@NotBlankCharSequenceNo不是null、trim后長度大于0
    非空@Null所有對象Yes是null
    長度@Size(min=0, max=Integer.MAX_VALUE)CharSequence, Collection, Map, ArrayYes字符串長度、集合size
    大小@PositiveBigDecimal, BigInteger, byte, short, int, long, float, doubleYes數字>0
    大小@PositiveOrZeroBigDecimal, BigInteger, byte, short, int, long, float, doubleYes數字>=0
    大小@NegativeBigDecimal, BigInteger, byte, short, int, long, float, doubleYes數字<0
    大小@NegativeOrZeroBigDecimal, BigInteger, byte, short, int, long, float, doubleYes數字<=0
    大小@Min(value=0L)BigDecimal, BigInteger, byte, short, int, longYes數字>=min.value
    大小@Max(value=0L)BigDecimal, BigInteger, byte, short, int, longYes數字<=max.value
    大小@Range(min=0L, max=Long.MAX_VALUE)BigDecimal, BigInteger, byte, short, int, longYesrange.min<=數字<=range.max
    大小@DecimalMin(value="")BigDecimal, BigInteger, CharSequence, byte, short, int, longYes數字>=decimalMin.value
    大小@DecimalMax(value="")BigDecimal, BigInteger, CharSequence, byte, short, int, longYes數字<=decimalMax.value
    日期@Past
    • java.util.Date

    • java.util.Calendar

    • java.time.Instant

    • java.time.LocalDate

    • java.time.LocalDateTime

    • java.time.LocalTime

    • java.time.MonthDay

    • java.time.OffsetDateTime

    • java.time.OffsetTime

    • java.time.Year

    • java.time.YearMonth

    • java.time.ZonedDateTime

    • java.time.chrono.HijrahDate

    • java.time.chrono.JapaneseDate

    • java.time.chrono.MinguoDate

    • java.time.chrono.ThaiBuddhistDate

    Yes時間在當前時間之前
    日期@PastOrPresent同上Yes時間在當前時間之前 或者等于此時
    日期@Future同上Yes時間在當前時間之后
    日期@FutureOrPresent同上Yes時間在當前時間之后 或者等于此時
    格式@Pattern(regexp="", flags={})CharSequenceYes匹配正則表達式
    格式@Email
    @Email(regexp=".*", flags={})
    CharSequenceYes匹配郵箱格式
    格式@Digts(integer=0, fraction=0)BigDecimal, BigInteger, CharSequence, byte, short, int, longYes必須是數字類型,且滿足整數位數<=digits.integer, 浮點位數<=digits.fraction
    布爾@AssertTruebooleanYes必須是true
    布爾@AssertFalsebooleanYes必須是false

    注: 后續還需補充Hibernate Validator中實現的constraints注解,如表中@Range。

    對象集成constraints示例

    /**
     * 用戶 - DTO
     *
     * @author luohq
     * @date 2021-09-04 13:45
     */
    public class UserDto {
    
        @NotNull(groups = Update.class)
        @Positive
        private Long id;
    
        @NotBlank
        @Size(max = 32)
        private String name;
    
        @NotNull
        @Range(min = 1, max = 2)
        private Integer sex;
    
        @NotBlank
        @Pattern(regexp = "^\\d{8,11}$")
        private String phone;
    
        @NotNull
        @Email
        private String mail;
    
        @NotNull
        @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$")
        private String birthDateStr;
    
        @NotNull
        @PastOrPresent
        private LocalDate birthLocalDate;
    
        @NotNull
        @Past
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private LocalDateTime registerLocalDatetime;
    
        @Valid
        @NotEmpty
        private List<OrgDto> orgs;
    
    	//省略getter、setter、toString方法	
    }
    
    /**
     * 組織 - DTO
     *
     * @author luohq
     * @date 2021-09-04 14:10
     */
    public class OrgDto {
        @NotNull
        @Positive
        private Long orgId;
    
        @NotBlank
        @Size(min = 1, max = 32)
        private String orgName;
        
        //省略getter、setter、toString方法	
    }

    注:

    • 可通過constraints注解的groups指定分組
      即指定constraints僅在指定group生效,默認均為Default分組,
      后續可通過@Validated({MyGroupInterface.class})形式進行分組的指定

    • 可通過@Valid注解進行級聯驗證(Cascaded Validation,即嵌套對象驗證)
      如上示例中@Valid添加在 List<OrgDto> orgs上,即會對list中的每個OrgDto進行驗證

    SpringBoot集成自動驗證

    參考:
    https://www.baeldung.com/javax-validation-method-constraints#validation

    集成maven依賴

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

    驗證RequestBody、Form對象參數

    在參數前加@Validated

    Java SpringBoot Validation怎么用

    驗證簡單參數

    在controller類上加@Validated

    Java SpringBoot Validation怎么用

    驗證指定分組

    Java SpringBoot Validation怎么用

    全局controller驗證異常處理

    通過@ControllerAdvice、@ExceptionHandler來對SpringBoot Validation驗證框架拋出的異常進行統一處理,
    并將錯誤信息拼接后統一返回,具體處理代碼如下:

    import com.luo.demo.validation.domain.result.CommonResult;
    import com.luo.demo.validation.enums.RespCodeEnum;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.http.HttpStatus;
    import org.springframework.validation.BindException;
    import org.springframework.validation.FieldError;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.ResponseStatus;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.validation.ConstraintViolationException;
    import java.util.List;
    import java.util.Optional;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    /**
     * controller增強 - 通用異常處理
     *
     * @author luohq
     * @date 2021-09-04 13:43
     */
    @ControllerAdvice
    public class ControllerAdviceHandler {
    
        private static final Logger log = LoggerFactory.getLogger(ControllerAdviceHandler.class);
    
        /**
         * 是否在響應結果中展示驗證錯誤提示信息
         */
        @Value("${spring.validation.msg.enable:true}")
        private Boolean enableValidationMsg;
    
        /**
         * 符號常量
         */
        private final String DOT = ".";
        private final String SEPARATOR_COMMA = ", ";
        private final String SEPARATOR_COLON = ": ";
    
        /**
         * 驗證異常處理 - 在@RequestBody上添加@Validated處觸發
         *
         * @param request
         * @param ex
         * @return
         */
        @ExceptionHandler({MethodArgumentNotValidException.class})
        @ResponseStatus(HttpStatus.OK)
        @ResponseBody
        public CommonResult handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException ex) {
            log.warn("{} - MethodArgumentNotValidException!", request.getServletPath());
            CommonResult commonResult = CommonResult.respWith(RespCodeEnum.PARAM_INVALID.getCode(), this.convertFiledErrors(ex.getBindingResult().getFieldErrors()));
            log.warn("{} - resp with param invalid: {}", request.getServletPath(), commonResult);
            return commonResult;
        }
    
        /**
         * 驗證異常處理 - form參數(對象參數,沒有加@RequestBody)觸發
         *
         * @param request
         * @param ex
         * @return
         */
        @ExceptionHandler({BindException.class})
        @ResponseStatus(HttpStatus.OK)
        @ResponseBody
        public CommonResult handleBindException(HttpServletRequest request, BindException ex) {
            log.warn("{} - BindException!", request.getServletPath());
            CommonResult commonResult = CommonResult.respWith(RespCodeEnum.PARAM_INVALID.getCode(), this.convertFiledErrors(ex.getFieldErrors()));
            log.warn("{} - resp with param invalid: {}", request.getServletPath(), commonResult);
            return commonResult;
        }
    
    
        /**
         * 驗證異常處理 - @Validated加在controller類上,
         * 且在參數列表中直接指定constraints時觸發
         *
         * @param request
         * @param ex
         * @return
         */
        @ExceptionHandler({ConstraintViolationException.class})
        @ResponseStatus(HttpStatus.OK)
        @ResponseBody
        public CommonResult handleConstraintViolationException(HttpServletRequest request, ConstraintViolationException ex) {
            log.warn("{} - ConstraintViolationException - {}", request.getServletPath(), ex.getMessage());
            CommonResult commonResult = CommonResult.respWith(RespCodeEnum.PARAM_INVALID.getCode(), this.convertConstraintViolations(ex));
            log.warn("{} - resp with param invalid: {}", request.getServletPath(), commonResult);
            return commonResult;
        }
    
        /**
         * 全局默認異常處理
         *
         * @param request
         * @param ex
         * @return
         */
        @ExceptionHandler({Throwable.class})
        @ResponseStatus(HttpStatus.OK)
        @ResponseBody
        public CommonResult handleException(HttpServletRequest request, Throwable ex) {
            log.warn("{} - Exception!", request.getServletPath(), ex);
            CommonResult commonResult = CommonResult.failed();
            log.warn("{} - resp failed: {}", request.getServletPath(), commonResult);
            return commonResult;
        }
    
        /**
         * 轉換FieldError列表為錯誤提示信息
         *
         * @param fieldErrors
         * @return
         */
        private String convertFiledErrors(List<FieldError> fieldErrors) {
            return Optional.ofNullable(fieldErrors)
                    .filter(fieldErrorsInner -> this.enableValidationMsg)
                    .map(fieldErrorsInner -> fieldErrorsInner.stream()
                            .flatMap(fieldError -> Stream.of(fieldError.getField(), SEPARATOR_COLON, fieldError.getDefaultMessage(), SEPARATOR_COMMA))
                            .collect(Collectors.joining()))
                    .map(msg -> msg.substring(0, msg.length() - SEPARATOR_COMMA.length()))
                    .orElse(null);
        }
    
        /**
         * 轉換ConstraintViolationException異常為錯誤提示信息
         *
         * @param constraintViolationException
         * @return
         */
        private String convertConstraintViolations(ConstraintViolationException constraintViolationException) {
            return Optional.ofNullable(constraintViolationException.getConstraintViolations())
                    .filter(constraintViolations -> this.enableValidationMsg)
                    .map(constraintViolations -> constraintViolations.stream()
                            .flatMap(constraintViolation -> {
                                String path = constraintViolation.getPropertyPath().toString();
                                path = path.substring(path.lastIndexOf(DOT) + 1);
                                String errMsg = constraintViolation.getMessage();
                                return Stream.of(path, SEPARATOR_COLON, errMsg, SEPARATOR_COMMA);
                            }).collect(Collectors.joining())
                    ).map(msg -> msg.substring(0, msg.length() - SEPARATOR_COMMA.length()))
                    .orElse(null);
    
        }
    }

    參數驗證未通過返回結果示例:

    Java SpringBoot Validation怎么用

    注: 其中CommonResult為統一返回結果,可根據自己業務進行調整

    Java SpringBoot Validation怎么用

    自定義constraints

    自定義field constraint注解主要分為以下幾步:
    (1)定義constraint annotation注解及其屬性
    (2)通過注解的元注解@Constraint(validatedBy = {})關聯的具體的驗證器實現
    (3)實現驗證器邏輯

    @DateFormat

    具體字符串日期格式constraint @DateFormat定義示例如下:

    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.*;
    import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
    
    /**
     * The annotated {@code CharSequence} must match date format.
     * The default date format is "yyyy-MM-dd".
     * Can override with property "format".
     * see {@link java.time.format.DateTimeFormatter}.
     * <p>
     * Accepts {@code CharSequence}. {@code null} elements are considered valid.
     *
     * @author luo
     * @date 2021-09-05
     */
    @Documented
    @Constraint(validatedBy = DateFormatValidator.class)
    @Target({ElementType.METHOD, ElementType.FIELD, ANNOTATION_TYPE,})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DateFormat {
        String message() default "日期格式不正確";
    
        String format() default "yyyy-MM-dd";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    }
    
    
    
    import org.springframework.util.StringUtils;
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    import java.time.format.DateTimeFormatter;
    
    /**
     * Date Format validator
     *
     * @author luohq
     * @date 2021-09-05
     */
    public class DateFormatValidator implements ConstraintValidator<DateFormat, String> {
    
        private String format;
    
        @Override
        public void initialize(DateFormat dateFormat) {
            this.format = dateFormat.format();
        }
    
        @Override
        public boolean isValid(String dateStr, ConstraintValidatorContext cxt) {
            if (!StringUtils.hasText(dateStr)) {
                return true;
            }
            try {
                DateTimeFormatter.ofPattern(this.format).parse(dateStr);
                return true;
            } catch (Throwable ex) {
                return false;
            }
        }
    }

    @PhoneNo

    在查看hbernate-validator中URL、Email約束實現時,發現可以通過元注解的形式去復用constraint實現(如@Pattern),故參考如上方式實現@PhoneNo約束

    import javax.validation.Constraint;
    import javax.validation.OverridesAttribute;
    import javax.validation.Payload;
    import javax.validation.ReportAsSingleViolation;
    import javax.validation.constraints.Pattern;
    import java.lang.annotation.Documented;
    import java.lang.annotation.Repeatable;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    import static java.lang.annotation.ElementType.*;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    
    /**
     * The annotated {@code CharSequence} must match phone no format.
     * The regular expression follows the Java regular expression conventions
     * see {@link java.util.regex.Pattern}.
     * <p>
     * Accepts {@code CharSequence}. {@code null} elements are considered valid.
     *
     * @author luo
     * @date 2021-09-05
     */
    @Documented
    @Constraint(validatedBy = {})
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    @Repeatable(PhoneNo.List.class)
    @ReportAsSingleViolation
    @Pattern(regexp = "")
    public @interface PhoneNo {
        String message() default "電話號碼格式不正確";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
        /**
         * @return an additional regular expression the annotated PhoneNo must match. The default is "^\\d{8,11}$"
         */
        @OverridesAttribute(constraint = Pattern.class, name = "regexp") String regexp() default "^\\d{8,11}$";
    
        /**
         * @return used in combination with {@link #regexp()} in order to specify a regular expression option
         */
        @OverridesAttribute(constraint = Pattern.class, name = "flags") Pattern.Flag[] flags() default {};
    
        /**
         * Defines several {@code @URL} annotations on the same element.
         */
        @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
        @Retention(RUNTIME)
        @Documented
        @interface List {
            PhoneNo[] value();
        }
    }

    注: 同理可以實現@IdNo約束

    使用自定義constraint注解

    可將之前的對象集成示例中代碼調整為使用自定義驗證注解如下:

    /**
     * 用戶 - DTO
     *
     * @author luohq
     * @date 2021-09-04 13:45
     */
    public class UserDto {
        ...
        @NotBlank
        //@Pattern(regexp = "^\\d{8,11}$")
        @PhoneNo
        private String phone;
        
        @NotBlank
        @IdNo
        private String idNo;
    
        @NotNull
        //@Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$")
        @DateFormat
        //@DateTimeFormat
        private String birthDateStr;
    
        ...
    }

    同時自定義constraints還支持跨多參數、驗證對象里的多個field、驗證返回對象等用法,待后續再詳細探索。

    問題

    通過在對象屬性、方法參數上標注注解的形式,需要侵入代碼,之前有的架構師不喜歡這種風格。
    在一方開發時,我們有全部源碼且在公司內部,這種方式還是可以的,且集成比較方便,
    但是依賴三方Api jar包(參數對象定義在jar包中),我們無法直接去修改參數對象,依舊使用這種侵入代碼的注解方式就不適用了,
    針對三方包、或者替代注解這種形式,之前公司內部有實現過基于xml配置的形式進行驗證,
    這種方式不侵入參數對象,且集成也還算方便,
    但是用起來還是沒有直接在代碼里寫注解來的順手(代碼有補全、有提示、程序員友好),
    所以一方開發時,首選推薦SpringBoot Validation這套體系,無法直接編輯參數對象時再考慮其他方式。

    參考:

    【自定義validator - field、class level】https://www.baeldung.com/spring-mvc-custom-validator

    【Spring boot集成validation、全局異常處理】https://www.baeldung.com/spring-boot-bean-validation

    【JSR380、非Spring框架集成validation】https://www.baeldung.com/javax-validation

    【方法約束 - Single param、Cross param、Return value自定義constraints、編程調用驗證】https://www.baeldung.com/javax-validation-method-constraints

    Spring Validation最佳實踐及其實現原理,參數校驗沒那么簡單!

    https://reflectoring.io/bean-validation-with-spring-boot/

    以上是“Java SpringBoot Validation怎么用”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    九寨沟县| 巴林右旗| 如东县| 突泉县| 江西省| 盐城市| 庐江县| 鹤壁市| 益阳市| 肇东市| 双江| 吉木乃县| 浦北县| 梁山县| 措勤县| 仪征市| 泽普县| 眉山市| 馆陶县| 珠海市| 兰坪| 枞阳县| 清镇市| 花莲县| 星座| 肥城市| 满洲里市| 邹平县| 沙河市| 陵水| 秦安县| 平利县| 蓬安县| 芜湖市| 凌源市| 贵德县| 平舆县| 买车| 即墨市| 台州市| 高陵县|