Jul 10, 2023
Jul 10, 2023
N/A Views
MD
warning
この記事は2年以上前に更新されたものです。情報が古くなっている可能性があります。

Warning

This article was automatically translated by OpenAI (gpt-4o-mini).It may be edited eventually, but please be aware that it may contain incorrect information at this time.

In Spring 6.1.0-M1, a functional Validator factory method was added (https://github.com/spring-projects/spring-framework/pull/29890).

As a result, converting YAVI's Validator to Spring's Validator has become easier. Therefore, YAVI has become easier to use in Controllers that perform validation with @Valid/@Validated.

Let's take the "Validating Form Input" guide from Spring's documentation as an example and change Bean Validation to YAVI.

First, let's define the validation.

The original definition is:

package com.example.validatingforminput;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public class PersonForm {

    @NotNull
    @Size(min=2, max=30)
    private String name;

    @NotNull
    @Min(18)
    private Integer age;

    // ...
}

When written with YAVI, for example:

package com.example.validatingforminput;

import am.ik.yavi.builder.ValidatorBuilder;
import am.ik.yavi.core.Validator;

public class PersonForm {

    public static Validator<PersonForm> validator = ValidatorBuilder.<PersonForm>of()
            .constraint(PersonForm::getName, "name", c -> c.notNull().greaterThanOrEqual(2).lessThanOrEqual(30))
            .constraint(PersonForm::getAge, "age", c -> c.notNull().greaterThanOrEqual(18))
            .build();

    // ...
}

This is how it looks.

The original code for using validation in the Controller is:

package com.example.validatingforminput;

import jakarta.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Controller
public class WebController implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/results").setViewName("results");
    }

    @GetMapping("/")
    public String showForm(PersonForm personForm) {
        return "form";
    }

    @PostMapping("/")
    public String checkPersonInfo(@Valid PersonForm personForm, BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            return "form";
        }

        return "redirect:/results";
    }
}

When using YAVI + Spring 6.1, it can be written as follows:

package com.example.validatingforminput;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Controller
public class WebController implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/results").setViewName("results");
    }

    @InitBinder
    void initBinder(WebDataBinder binder) {
        Validator personValidator = Validator.forInstanceOf(PersonForm.class, PersonForm.validator.toBiConsumer(Errors::rejectValue));
        binder.addValidators(personValidator);
    }

    @GetMapping("/")
    public String showForm(PersonForm personForm) {
        return "form";
    }

    @PostMapping("/")
    public String checkPersonInfo(@Validated PersonForm personForm, BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            return "form";
        }

        return "redirect:/results";
    }
}

The following part utilizes the factory method available in Spring 6.1:

Validator personValidator = Validator.forInstanceOf(PersonForm.class, PersonForm.validator.toBiConsumer(Errors::rejectValue));

In the above example, Bean Validation and spring-boot-starter-validation are excluded from the dependencies. Therefore, @Validated (Spring's annotation) is used instead of @Valid (Bean Validation's annotation). Both can be used.

The overall diff can be found at https://github.com/making/gs-validating-form-input/commit/7261b26bf94c4aae86b52c68f9f78380e07a79f3.

The diff for just the Controller is as follows:

image

The Controller code can now use YAVI instead of Bean Validation without significant changes.


By the way, as mentioned in the documentation, YAVI has been easily usable with Spring MVC even before this.

In this example, it can be written as follows:

    @PostMapping("/")
    public String checkPersonInfo(@Validated PersonForm personForm, BindingResult bindingResult) {
        ConstraintViolations violations = PersonForm.validator.validate(personForm);
        if (!violations.isValid()) {
            violations.apply(bindingResult::rejectValue);
            return "form";
        }

        return "redirect:/results";
    }

or

    @PostMapping("/")
    public String checkPersonInfo(@Validated PersonForm personForm, BindingResult bindingResult) {
        return PersonForm.validator.applicative()
                .validate(personForm)
                .fold(violations -> {
                    ConstraintViolations.of(violations).apply(bindingResult::rejectValue);
                    return "form";
                }, form -> "redirect:/results");
    }

This update has particularly enhanced compatibility for those who prefer the programming model using @Valid/@Validated.

Found a mistake? Update the entry.
Share this article: