ホームページ  >  記事  >  Java  >  SpringBootパラメータ検証の手法は何ですか?

SpringBootパラメータ検証の手法は何ですか?

WBOY
WBOY転載
2023-05-12 08:19:241294ブラウズ

1. 検証アノテーションを使用する

Spring Boot には組み込みの検証アノテーションが用意されており、null または空のフィールドのチェックや強制実行など、入力フィールドを簡単かつ迅速に検証できます。長さの制限、正規表現を使用したパターンの検証、および電子メール アドレスの検証。

最も一般的に使用される検証アノテーションには、次のようなものがあります。

  • @NotNull: 指定されたフィールドを null にすることはできません。

  • @NotEmpty: 指定されたリスト フィールドを空にすることはできません。

  • @NotBlank: 文字列フィールドが空であってはいけない、またはスペースのみが含まれていてはいけないことを指定します。

  • @Min および @Max: 数値フィールドの最小値と最大値を指定します。

  • @Pattern: 文字列フィールドが一致する必要がある正規表現パターンを指定します。

  • @Email: 文字列フィールドが有効な電子メール アドレスである必要があることを指定します。

具体的な使用法については、次の例を参照してください:

public class User {
    @NotNull
    private Long id;

    @NotBlank
    @Size(min = 2, max = 50)
    private String firstName;

    @NotBlank
    @Size(min = 2, max = 50)
    private String lastName;

    @Email
    private String email;

    @NotNull
    @Min(18)
    @Max(99)
    private Integer age;

    @NotEmpty
    private List<String> hobbies;

    @Pattern(regexp = "[A-Z]{2}\d{4}")
    private String employeeId;

2. カスタム検証アノテーションを使用する

Spring Boot の組み込み検証アノテーションは便利ですが、すべての状況をカバーしているわけではありません。特別なパラメーター検証シナリオがある場合は、Spring の JSR 303 検証フレームワークを使用してカスタム検証アノテーションを作成できます。カスタム アノテーションを使用すると、検証ロジックの再利用性と保守性が向上します。

ユーザーが投稿を作成できるアプリケーションがあるとします。各投稿にはタイトルと本文が必要であり、タイトルはすべての投稿で一意である必要があります。 Spring Boot は、フィールドが空かどうかをチェックするための組み込みの検証アノテーションを提供しますが、一意性をチェックするための組み込みの検証アノテーションは提供しません。この場合、この状況に対処するカスタム検証アノテーションを作成できます。

最初に、カスタム制約アノテーション UniqueTitle:

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueTitleValidator.class)
public @interface UniqueTitle {
    String message() default "Title must be unique";

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

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

を作成します。次に、データベースから取得する目的で PostRepository インターフェイスを作成します。投稿:

public interface PostRepository extends JpaRepository<Post, Long> {
    Post findByTitle(String title);
}

次に、バリデーター クラス UniqueTitleValidator を次のように定義する必要があります。

@Component
public class UniqueTitleValidator implements ConstraintValidator<UniqueTitle, String> {

    @Autowired
    private PostRepository postRepository;

    @Override
    public boolean isValid(String title, ConstraintValidatorContext context) {
        if (title == null) {
            return true;
        }
        return Objects.isNull(postRepository.findByTitle(title));
    }
}

UniqueTitleValidatorConstraintValidatorInterface を実装します。 , これには 2 つのジェネリック タイプがあります: 1 つ目はカスタム アノテーション UniqueTitle、2 つ目は検証されるフィールド タイプ (この場合は String) です。 PostRepository クラスを使用してデータベースから投稿を取得します。

isValid() メソッドは、title が null かどうか、または PostRepository をクエリすることで一意であるかどうかを確認します。 title が null または一意の場合、検証は成功し、true が返されます。

カスタム検証アノテーションとバリデーター クラスが定義されているので、それを使用して Spring Boot アプリケーションの投稿タイトルを検証できます。

public class Post {
    @UniqueTitle
    private String title;

    @NotNull
    private String body;
}

これには

@UniqueTitle注釈は、Post クラスの title 変数に適用されます。このフィールドを検証すると、UniqueTitleValidator クラスで定義された検証ロジックがトリガーされます。

3. サーバー側での検証

フロントエンドまたはクライアントの検証に加えて、サーバー側での入力の検証が重要です。これにより、悪意のあるデータまたは不正な形式のデータが処理または保存される前に確実に捕捉されます。これは、アプリケーションのセキュリティと安定性にとって重要です。

ユーザーが新しいアカウントを作成できるようにする

REST エンドポイントがあるとします。エンドポイントは、ユーザーのユーザー名とパスワードを含む JSON リクエスト本文を期待します。入力が有効であることを確認するには、DTO (データ転送オブジェクト) クラスを作成し、そのフィールドに検証アノテーションを適用します。

public class UserDTO {

    @NotBlank
    private String username;

    @NotBlank
    private String password;
}

@NotBlank アノテーションを使用して、 を確認します。 username および password フィールドは空または null ではありません。

次に、HTTP POST リクエストを処理するコントローラー メソッドを作成し、新しいユーザーを作成する前に入力を検証します。

@RestController
@RequestMapping("/users")
@Validated
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping
    public ResponseEntity<String> createUser(@Valid @RequestBody UserDTO userDto) {
        userService.createUser(userDto);
        return ResponseEntity.status(HttpStatus.CREATED).body("User created successfully");
    }
}

Spring の

@Validated アノテーションを使用します。メソッドレベルの検証を有効にするには、@Valid アノテーションを userDto パラメータに適用して、検証プロセスをトリガーします。

4.意味のあるエラー メッセージを提供する

検証が失敗した場合は、何が問題で、その修正方法を説明する明確かつ簡潔なエラー メッセージを提供する必要があります。

これは、ユーザーが新しいユーザーを作成できる

RESTful API がある場合の例です。名前と電子メール アドレスのフィールドが空ではなく、18 歳から 99 歳までであることを確認します。これらのフィールドに加えて、ユーザーがアカウントを作成しようとすると、明確なエラー メッセージまたは「電子メール」も表示されます。重複した「ユーザー名」.メール」。

これを行うには、次のように必要な検証アノテーションを備えたモデル クラス User を定義できます。

public class User {

    @NotBlank(message = "用户名不能为空")
    private String name;

    @NotBlank(message = "Email不能为空")
    @Email(message = "无效的Emaild地址")
    private String email;

    @NotNull(message = "年龄不能为空")
    @Min(value = 18, message = "年龄必须大于18")
    @Max(value = 99, message = "年龄必须小于99")
    private Integer age;
}

message 属性を使用して、各検証アノテーションにカスタム エラー メッセージを提供します。

次に、Spring コントローラーでフォームの送信を処理し、

@Valid アノテーションを使用してユーザー入力を検証します。

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping
    public ResponseEntity<String> createUser(@Valid @RequestBody User user, BindingResult result) {
        if (result.hasErrors()) {
            List<String> errorMessages = result.getAllErrors().stream()
                    .map(DefaultMessageSourceResolvable::getDefaultMessage)
                    .collect(Collectors.toList());
            return ResponseEntity.badRequest().body(errorMessages.toString());
        }

        // save the user to the database using UserService
        userService.saveUser(user);

        return ResponseEntity.status(HttpStatus.CREATED).body("User created successfully");
    }
}

ここでは

@ Valid を使用します。 アノテーションを使用して User オブジェクトの検証をトリガーし、BindingResult オブジェクトを使用して検証エラーをキャッチします。

5.将 i18n 用于错误消息

如果你的应用程序支持多种语言,则必须使用国际化 (i18n) 以用户首选语言显示错误消息。

以下是在 Spring Boot 应用程序中使用 i18n 处理错误消息的示例

首先,在资源目录下创建一个包含默认错误消息的 messages.properties 文件

# messages.properties
user.name.required=Name is required.
user.email.invalid=Invalid email format.
user.age.invalid=Age must be a number between 18 and 99.

接下来,为每种支持的语言创建一个 messages_xx.properties 文件,例如,中文的 messages_zh_CN.properties

user.name.required=名称不能为空.
user.email.invalid=无效的email格式.
user.age.invalid=年龄必须在18到99岁之间.

然后,更新您的验证注释以使用本地化的错误消息

public class User {
    @NotNull(message = "{user.id.required}")
    private Long id;

    @NotBlank(message = "{user.name.required}")
    private String name;

    @Email(message = "{user.email.invalid}")
    private String email;

    @NotNull(message = "{user.age.required}")
    @Min(value = 18, message = "{user.age.invalid}")
    @Max(value = 99, message = "{user.age.invalid}")
    private Integer age;
}

最后,在 Spring 配置文件中配置 MessageSource bean 以加载 i18n 消息文件

@Configuration
public class AppConfig {
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }

    @Bean
    public LocalValidatorFactoryBean validator() {
        LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
        validatorFactoryBean.setValidationMessageSource(messageSource());
        return validatorFactoryBean;
    }
}

现在,当发生验证错误时,错误消息将根据随请求发送的“Accept-Language”标头以用户的首选语言显示。

6.使用分组验证

验证组是 Spring Boot 验证框架的一个强大功能,允许您根据其他输入值或应用程序状态应用条件验证规则。

现在有一个包含三个字段的User类的情况下:firstNamelastNameemail。我们要确保如果 email 字段为空,则 firstNamelastName 字段必须非空。否则,所有三个字段都应该正常验证。

为此,我们将定义两个验证组:EmailNotEmptyDefaultEmailNotEmpty 组将包含当 email 字段不为空时的验证规则,而 Default 组将包含所有三个字段的正常验证规则。

创建带有验证组的 User

public class User {
    @NotBlank(groups = Default.class)
    private String firstName;

    @NotBlank(groups = Default.class)
    private String lastName;

    @Email(groups = EmailNotEmpty.class)
    private String email;

    // getters and setters omitted for brevity
    public interface EmailNotEmpty {}
    public interface Default {}
}

请注意,我们在User类中定义了两个接口,EmailNotEmptyDefault。这些将作为我们的验证组。

接下来,我们更新Controller使用这些验证组

@RestController
@RequestMapping("/users")
@Validated
public class UserController {
    public ResponseEntity<String> createUser(
            @Validated({org.example.model.ex6.User.EmailNotEmpty.class}) @RequestBody User userWithEmail,
            @Validated({User.Default.class}) @RequestBody User userWithoutEmail)
    {
        // Create the user and return a success response
       
    }
}

我们已将@Validated注释添加到我们的控制器,表明我们想要使用验证组。我们还更新了 createUser 方法,将两个 User 对象作为输入,一个在 email 字段不为空时使用,另一个在它为空时使用。

@Validated 注释用于指定将哪个验证组应用于每个 User 对象。对于 userWithEmail 参数,我们指定了 EmailNotEmpty 组,而对于 userWithoutEmail 参数,我们指定了 Default 组。

进行这些更改后,现在将根据“电子邮件”字段是否为空对“用户”类进行不同的验证。如果为空,则 firstNamelastName 字段必须非空。否则,所有三个字段都将正常验证。

7.对复杂逻辑使用跨域验证

如果需要验证跨多个字段的复杂输入规则,可以使用跨字段验证来保持验证逻辑的组织性和可维护性。跨字段验证可确保所有输入值均有效且彼此一致,从而防止出现意外行为。

假设我们有一个表单,用户可以在其中输入任务的开始日期和结束日期,并且我们希望确保结束日期不早于开始日期。我们可以使用跨域验证来实现这一点。

首先,我们定义一个自定义验证注解EndDateAfterStartDate

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EndDateAfterStartDateValidator.class)
public @interface EndDateAfterStartDate {
    String message() default "End date must be after start date";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

然后,我们创建验证器EndDateAfterStartDateValidator

public class EndDateAfterStartDateValidator implements ConstraintValidator<EndDateAfterStartDate, TaskForm> {
    @Override
    public boolean isValid(TaskForm taskForm, ConstraintValidatorContext context) {
        if (taskForm.getStartDate() == null || taskForm.getEndDate() == null) {
            return true;
        }

        return taskForm.getEndDate().isAfter(taskForm.getStartDate());
    }
}

最后,我们将EndDateAfterStartDate注释应用于我们的表单对象TaskForm

@EndDateAfterStartDate
public class TaskForm {
    @NotNull
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate startDate;

    @NotNull
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate endDate;
}

现在,当用户提交表单时,验证框架将自动检查结束日期是否晚于开始日期,如果不是,则提供有意义的错误消息。

8.对验证错误使用异常处理

可以使用异常处理ExceptionHandler来统一捕获和处理验证错误。

以下是如何在 Spring Boot 中使用异常处理来处理验证错误的示例:

@RestControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
                                                                  HttpHeaders headers, HttpStatus status,
                                                                  WebRequest request) {
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("timestamp", LocalDateTime.now());
        body.put("status", status.value());

        // Get all errors
        List<String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(x -> x.getDefaultMessage())
                .collect(Collectors.toList());

        body.put("errors", errors);

        return new ResponseEntity<>(body, headers, status);
    }
}

在这里,我们创建了一个用 @RestControllerAdvice 注解的 RestExceptionHandler 类来处理我们的 REST API 抛出的异常。然后我们创建一个用 @ExceptionHandler 注解的方法来处理在验证失败时抛出的 MethodArgumentNotValidException

在处理程序方法中,我们创建了一个 Map 对象来保存错误响应的详细信息,包括时间戳、HTTP 状态代码和错误消息列表。我们使用 MethodArgumentNotValidException 对象的 getBindingResult() 方法获取所有验证错误并将它们添加到错误消息列表中。

最后,我们返回一个包含错误响应详细信息的ResponseEntity对象,包括作为响应主体的错误消息列表、HTTP 标头和 HTTP 状态代码。

有了这个异常处理代码,我们的 REST API 抛出的任何验证错误都将被捕获并以结构化和有意义的格式返回给用户,从而更容易理解和解决问题。

9.测试你的验证逻辑

需要为你的验证逻辑编写单元测试,以帮助确保它正常工作。

@DataJpaTest
public class UserValidationTest {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private Validator validator;

    @Test
    public void testValidation() {
        User user = new User();
        user.setFirstName("John");
        user.setLastName("Doe");
        user.setEmail("invalid email");

        Set<ConstraintViolation<User>> violations = validator.validate(user);

        assertEquals(1, violations.size());
        assertEquals("must be a well-formed email address", violations.iterator().next().getMessage());
    }
}

我们使用 JUnit 5 编写一个测试来验证具有无效电子邮件地址的“用户”对象。然后我们使用 Validator 接口来验证 User 对象并检查是否返回了预期的验证错误。

10.考虑客户端验证

客户端验证可以通过向用户提供即时反馈并减少对服务器的请求数量来改善用户体验。但是,不应依赖它作为验证输入的唯一方法。客户端验证很容易被绕过或操纵,因此必须在服务器端验证输入,以确保安全性和数据完整性。

以上がSpringBootパラメータ検証の手法は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。