Spring Validationを使用してパラメーターの検証を適切に実行する

前書き

通常のビジネス開発プロセスでのコントローラーレイヤーのパラメーター検証の記述方法がわかりません。次のような直接的な判断はありますか?

public String add(UserVO userVO) {
    if(userVO.getAge() == null){
        return "年龄不能为空";
    }
    if(userVO.getAge() > 120){
        return "年龄不能超过120";
    }
    if(userVO.getName().isEmpty()){
        return "用户名不能为空";
    }
    // 省略一堆参数校验...
    return "OK";
}

ビジネスコードはまだ書かれておらず、光学パラメータの検証のために多くの判断が書かれています。このように書くことには何の問題もありませんが、エレガントでプロフェッショナルではないという印象を人々に与えます。

実際、Springフレームワークはすでに一連の検証コンポーネントである検証をカプセル化しています。その特徴は使いやすく、自由度が高いことです。次のレッスンでは、springboot-2.3.1.RELEASE単純なWebプロジェクト構築する方法を説明します。開発プロセス中にパラメーターをエレガントにチェックする方法について、順を追って説明します。

1.環境設定

springboot-2.3始まり、検証パッケージは、独立したとしてされているstarter次の依存関係を導入する必要があるので、コンポーネント:

<!--校验组件-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--web组件-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

springboot-2.3以前のバージョンでは、Webの依存関係を導入する必要があります。

2.小​​さなテスト

パラメータの検証は非常に簡単です。まず、検証するフィールドに検証ルールの注釈を追加します

public class UserVO {
    @NotNull(message = "age 不能为空")
    private Integer age;
}

次に、controllerメソッド@Validated追加し、エラーメッセージの受信に使用するためBindingResult、最初のバージョンがあります。

public String add1(@Validated UserVO userVO, BindingResult result) {
    List<FieldError> fieldErrors = result.getFieldErrors();
    if(!fieldErrors.isEmpty()){
        return fieldErrors.get(0).getDefaultMessage();
    }
    return "OK";
}

ツール(postman)を使用してインターフェースをリクエストします。パラメーターがルールを満たしていない場合、対応するmessage情報返されます:

age 不能为空

多くの組み込み検証アノテーションがあり、次のようにリストされています。

注釈 チェック機能
@AssertFalse 偽でなければならない
@AssertTrue 本当でなければならない
@DecimalMax 与えられた値以下
@ DecimalMin#@ DecimalMin# 与えられた値以上
@Digits 整数の最大数と小数の最大数を設定できます
@Eメール メール形式に準拠しているか確認してください
@未来 未来でなければなりません
@FutureOrPresent 現在または未来の時間
@Max マックス
@Min 最小
@負 負の数(0を含まない)
あずきっく 負または0
@NotBlank nullではなく、空白以外の文字が少なくとも1つ含まれている
@空ではない nullでも空でもない
@NotNull nullではない
@ヌル 無効です
@過去 過去でなければなりません
@PastOrPresent 現在を含む過去でなければなりません
@PositiveOrZero 正または0
@サイズ コンテナ内の要素の数を確認してください

3.標準の戻り値

検証するパラメーターがさらに増えたら、すべての検証失敗情報を一度に返して、インターフェイスの呼び出し側が調整しやすくします。これには、統一された戻り形式が必要です。一般的なものは、結果クラスをカプセル化することです。

public class ResultInfo<T>{
    private Integer status;
    private String message;
    private T response;
    // 省略其他代码...
}

controllerメソッド、第2版を変更します。

public ResultInfo add2(@Validated UserVO userVO, BindingResult result) {
    List<FieldError> fieldErrors = result.getFieldErrors();
    List<String> collect = fieldErrors.stream()
            .map(o -> o.getDefaultMessage())
            .collect(Collectors.toList());
    return new ResultInfo<>().success(400,"请求参数错误",collect);
}

このメソッドが要求されると、すべてのエラーパラメータが返されます。

{
    "status": 400,
    "message": "请求参数错误",
    "response": [
        "年龄必须在[1,120]之间",
        "bg 字段的整数位最多为3位,小数位最多为1位",
        "name 不能为空",
        "email 格式错误"
    ]
}

4.グローバル例外処理

それぞれのController方法でBindingResult情報処理を書くと、やはり使いづらい。チェック例外は、グローバル例外処理を通じて均一に処理できます。

@validatedコメントを書いて書いBindingResultいない場合、Springは例外をスローします。その結果、この検証例外を均一に処理するグローバル例外処理クラスを作成できるため、例外情報を整理するためにコードを繰り返す必要がなくなります。

グローバル例外処理クラスはクラス@RestControllerAdviceマークする必要があるだけであり、対応する例外処理メソッドで@ExceptionHandler注釈を使用して、処理する例外を指定ます。

@RestControllerAdvice
public class GlobalControllerAdvice {
    private static final String BAD_REQUEST_MSG = "客户端请求参数错误";
    // <1> 处理 form data方式调用接口校验失败抛出的异常 
    @ExceptionHandler(BindException.class)
    public ResultInfo bindExceptionHandler(BindException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> collect = fieldErrors.stream()
                .map(o -> o.getDefaultMessage())
                .collect(Collectors.toList());
        return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect);
    }
    // <2> 处理 json 请求体调用接口校验失败抛出的异常 
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultInfo methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> collect = fieldErrors.stream()
                .map(o -> o.getDefaultMessage())
                .collect(Collectors.toList());
        return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect);
    }
    // <3> 处理单个参数校验失败抛出的异常
    @ExceptionHandler(ConstraintViolationException.class)
    public ResultInfo constraintViolationExceptionHandler(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        List<String> collect = constraintViolations.stream()
                .map(o -> o.getMessage())
                .collect(Collectors.toList());
        return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect);
    }
    

}

実際、グローバルな例外処理クラスでは、複数の例外処理メソッドを記述できます。クラスの代表者は、3つのパラメータ検証中にスローされる可能性のある例外をまとめました。

  1. フォームデータメソッドを使用してインターフェイスを呼び出すと、検証例外によってBindExceptionがスローされます

  2. jsonリクエストボディを使用してインターフェースを呼び出し、検証例外がMethodArgumentNotValidExceptionをスローします

  3. 単一パラメーター検証例外がConstraintViolationExceptionをスローする

注:単一パラメーター検証では、パラメーターに検証メモを追加し、クラスにマークを付ける必要があります@Validated

グローバル例外処理クラスは、一対Exception.classの例外処理の追加など、処理する必要のあるさまざまな例外を追加できます。すべてをExceptionHandler処理できない場合は、例外情報を記録し、わかりやすいプロンプトを返します。

5.グループチェック

同じパラメーターに対して異なるシナリオで異なる検証ルールを適用する必要がある場合は、パケット検証を使用する必要があります。例:新しく登録されたユーザーが名前を指定していない場合、nameフィールドを空にすることができますが、名前を空の文字に更新することはできません。

グループ検証には3つのステップがあります。

  1. グループ化クラス(またはインターフェース)を定義する

  2. 検証アノテーションにgroups属性指定のグループを追加します

  3. Controllerメソッド@Validatedアノテーションにグループ化クラスを追加する

public interface Update extends Default{
}
public class UserVO {
    @NotBlank(message = "name 不能为空",groups = Update.class)
    private String name;
    // 省略其他代码...
}
@PostMapping("update")
public ResultInfo update(@Validated({Update.class}) UserVO userVO) {
    return new ResultInfo().success(userVO);
}

注意深い学生は、カスタムUpdateグループインターフェイスがインターフェイスを継承していることに気づいたかもしれませんDefault検証アノテーション(例:) @NotBlank@validatedデフォルトDefault.classは、javax.validation.groups.Defaultアノテーションで説明さているグループに属しています。

/**
 * Default Jakarta Bean Validation group.
 * <p>
 * Unless a list of groups is explicitly defined:
 * <ul>
 *     <li>constraints belong to the {@code Default} group</li>
 *     <li>validation applies to the {@code Default} group</li>
 * </ul>
 * Most structural constraints should belong to the default group.
 *
 * @author Emmanuel Bernard
 */
public interface Default {
}

Updateパケットインターフェイスを作成する場合、継承されたDefault場合、次の2つの書き込みは同等です。

@Validated({Update.class})

@Validated({Update.class,Default.class})

フィールドが検証される/updateだけでなく、nameデフォルトでDefault.classグループに属する他のフィールドも検証されることを確認するようインターフェースに要求します

{
    "status": 400,
    "message": "客户端请求参数错误",
    "response": [
        "name 不能为空",
        "age 不能为空",
        "email 不能为空"
    ]
}

それがされている場合はUpdate継承されていないDefault@Validated({Update.class})唯一のUpdate.classパラメータフィールドに属するグループがチェックされます。。変更後、インターフェースは次のような結果を得るために再び要求されたあなたは、他のフィールドがチェックに関与していないことがわかります。

{
    "status": 400,
    "message": "客户端请求参数错误",
    "response": [
        "name 不能为空"
    ]
}

6.再帰的な検証

OrderVOクラスの属性がUserVOクラスに追加され、OrderVOの属性も検証する必要がある場合、再帰的な検証が使用されます。これ@Validは、対応するプロパティに注釈を追加することで実現できます(コレクションにも同じことが当てはまります)。

OrderVOクラスは次のとおりです

public class OrderVO {
    @NotNull
    private Long id;
    @NotBlank(message = "itemName 不能为空")
    private String itemName;
    // 省略其他代码...
}

タイプOrderVOの属性をUserVOクラスに追加します。

public class UserVO {
    @NotBlank(message = "name 不能为空",groups = Update.class)
    private String name;
    //需要递归校验的OrderVO
    @Valid
    private OrderVO orderVO;
    // 省略其他代码...
}   

呼び出し要求の検証は次のとおりです。

7.カスタムチェック

Springの検証は非常に多くの機能を提供し、日々の開発におけるほとんどのパラメーター検証シナリオに対応できます。ただし、優れたフレームワークは簡単に拡張できる必要があります。スケーラビリティを使用すると、より複雑なビジネスシナリオに対処できます。結局のところ、開発プロセスでは、唯一の不変は変更自体です。

Spring Validationを使用すると、ユーザーは検証をカスタマイズできます。実装は2つのステップで非常に簡単です。

  1. カスタムチェックアノテーション

  2. 検証クラスを書く

コードも非常にシンプルで、コメントを一目で理解できます

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {HaveNoBlankValidator.class})// 标明由哪个类执行校验逻辑
public @interface HaveNoBlank {
 
    // 校验出错时默认返回的消息
    String message() default "字符串中不能含有空格";

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

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

    /**
     * 同一个元素上指定多个该注解时使用
     */
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    public @interface List {
        NotBlank[] value();
    }
}
public class HaveNoBlankValidator implements ConstraintValidator<HaveNoBlank, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // null 不做检验
        if (value == null) {
            return true;
        }
        if (value.contains(" ")) {
            // 校验失败
            return false;
        }
        // 校验成功
        return true;
    }
}

カスタム検証アノテーションは組み込みのアノテーションと同じです。必要なフィールドに対応するアノテーションを追加するだけで、学生は自分で検証できます

レビュー

上記は、Spring Validationを使用してパラメーターをエレガントに検証する方法の全体的な内容です。以下は、記事で言及されている検証機能に焦点を当てています

  1. 組み込みのさまざまな一般的な検証ノート

  2. 単一パラメーター検証をサポート

  3. 例外を自動的にアセンブルおよび検証するグローバル例外処理と組み合わせる

  4. パケットチェック

  5. 再帰的チェックをサポート

  6. カスタムチェック

方法はありませんが、テクニックは達成できます。方法がない場合は、テクニックで終わります

みんながJava Wayパブリックアカウントをフォローすることを歓迎します

良い記事、私は読んでいます❤️

おすすめ

転載: blog.csdn.net/hollis_chuang/article/details/108687966