Полная стратегия проверки параметров Hibernate Validator

Spring Boot Java

1. Введение

Поля данных обычно соответствуют бизнес-требованиям и дизайну базы данных, поэтому необходима внутренняя проверка параметров, и приложение должно использовать некоторые средства для обеспечения семантической правильности входных данных.

2. Болевые точки проверки данных

Чтобы обеспечить правильную семантику данных, нам нужно сделать много суждений, чтобы иметь дело с логикой проверки. Кроме того, многоуровневость проекта также приведет к повторным проверкам, что приведет к большому количеству кодов, не связанных с бизнесом. Это не способствует сопровождению кода и увеличивает нагрузку на разработчиков.

3. Спецификация проверки JSR 303 и ее реализация

Чтобы решить вышеупомянутые болевые точки, необходимо связать логику проверки к соответствующей модели домена. По этой причинеJSR 303 - спецификация валидации фасоли.Hibernate ValidatorдаJSR-303Эталонная реализация , которая обеспечиваетJSR 303Реализация всех ограничений в спецификации с добавлением некоторых расширений.

Общие аннотации ограничений, предоставляемые Hibernate Validator

Аннотация ограничения подробности
@Null Аннотируемый элемент должен бытьnull
@NotNull Аннотируемый элемент не должен бытьnull
@AssertTrue Аннотируемый элемент должен бытьtrue
@AssertFalse Аннотируемый элемент должен бытьfalse
@Min(value) Аннотированный элемент должен быть числом, значение которого должно быть больше или равно указанному минимальному значению.
@Max(value) Аннотированный элемент должен быть числом, значение которого должно быть меньше или равно указанному максимальному значению.
@DecimalMin(value) Аннотированный элемент должен быть числом, значение которого должно быть больше или равно указанному минимальному значению.
@DecimalMax(value) Аннотированный элемент должен быть числом, значение которого должно быть меньше или равно указанному максимальному значению.
@Size(max, min) Размер аннотируемого элемента должен быть в пределах указанного диапазона.
@Digits (integer, fraction) Аннотируемый элемент должен быть числом, и его значение должно быть в допустимом диапазоне.
@Past Аннотированный элемент должен быть датой в прошлом
@Future Аннотированный элемент должен быть датой в будущем
@Pattern(value) Аннотированный элемент должен соответствовать указанному регулярному выражению.
@Email Аннотированный элемент должен быть адресом электронной почты.
@Length Размер аннотируемой строки должен быть в пределах указанного диапазона.
@NotEmpty Аннотированная строка должна быть непустой.
@Range Аннотируемый элемент должен находиться в соответствующей области

4. Подтвердите использование аннотаций

существуетSpring Bootиспользовать в разработкеHibernate Validatorочень легко ввести следующееstarterВ теме:

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

Интерфейс, который можно реализовать для настройкиValidator, один из них заключается в использовании аннотаций ограничений. Fat Brother считает, что аннотации могут удовлетворить большинство потребностей, поэтому рекомендуется использовать аннотации для проверки данных. И аннотации более гибкие, и детализация управления более тонкая. Далее давайте узнаем, как использовать аннотации для проверки данных.

4.1 Основное использование аннотаций ограничений

Мы помечаем входные параметры методов, которые необходимо проверить, аннотационными ограничениями, Примеры следующие:

@Data
public class Student {

    @NotBlank(message = "姓名必须填")
    private String name;
    @NotNull(message = "年龄必须填写")
    @Range(min = 1,max =50, message = "年龄取值范围1-50")
    private Integer age;
    @NotEmpty(message = "成绩必填")
    private List<Double> scores;
}

POST-запрос

затем определитеPOSTпросилSpring MVCинтерфейс:

@RestController
@RequestMapping("/student")
public class StudentController {

    
    @PostMapping("/add")
    public Rest<?> addStudent(@Valid @RequestBody Student student) {
        return RestBody.okData(student);
    }
}   

через паруaddStudentДобавить параметры метода@Validчтобы включить проверку параметров. Когда запрос сделан со следующими данными, он выдастMethodArgumentNotValidExceptionисключение, подсказкаageСпектр1-50.

POST /student/add HTTP/1.1
Host: localhost:8888
Content-Type: application/json

{
    "name": "felord.cn",
    "age": 77,
    "scores": [
        55
    ]
}

ПОЛУЧИТЬ запрос

Делая то же самое, мы определяемGETЗапрашиваемый интерфейс:

@GetMapping("/get")
public Rest<?> getStudent(@Valid Student student) {
    return RestBody.okData(student);
}

Используйте приведенный ниже запрос, чтобы правильно оценивать учащихсяscoresПроверено, но то, что выброшено, неMethodArgumentNotValidExceptionисключение, ноBindExceptionаномальный. это и использовать@RequestBodyАннотации связаны, что очень важно для нашей последующей унифицированной обработки.

GET /student/get?name=felord.cn&age=12 HTTP/1.1
Host: localhost:8888

пользовательская аннотация

Может быть, некоторые ученики заметили, что я обозначил вышеприведенный возраст так:

@NotNull(message = "年龄必须填写")
@Range(min = 1,max =50, message = "年龄取值范围1-50")
private Integer age;

Это потому что@RangeОн не проверяет пустой регистр, а только проверяет, соответствует ли он ограничению диапазона, когда он не пуст. Поэтому используйте несколько аннотаций для ограничения. Если нам нужно многократно объединять несколько аннотаций для использования в некоторых сценариях, мы можем использовать пользовательские аннотации для их инкапсуляции и объединения.@NotNullа также@RangeПосле комбинации вы можете сымитировать одну и использовать ее, чтобы увидеть.

import org.hibernate.validator.constraints.Range;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.NotNull;
import javax.validation.constraintvalidation.SupportedValidationTarget;
import javax.validation.constraintvalidation.ValidationTarget;
import java.lang.annotation.*;

/**
 * @author a
 * @since 17:31
 **/
@Constraint(
        validatedBy = {}
)
@SupportedValidationTarget({ValidationTarget.ANNOTATED_ELEMENT})
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, 
        ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, 
        ElementType.PARAMETER, ElementType.TYPE_USE})
@NotNull
@Range(min = 1, max = 50)
@Documented
@ReportAsSingleViolation
public @interface Age {
    // message 必须有
    String message() default "年龄必须填写,且范围为 1-50 ";

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

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

Также есть случай, когда мы определяем значения перечисления в фоновом режиме для выполнения потока состояний, который также необходимо проверить.Например, мы определяем перечисление цветов:

public enum Colors {

    RED, YELLOW, BLUE

}

Надеемся, что участие не превыситColorsспектр["RED", "YELLOW", "BLUE"], что требует реализацииConstraintValidator<A extends Annotation, T>интерфейс для определения ограничения цвета, где дженерикиAАннотации для пользовательских ограничений, дженериковTДля типа входного параметра здесь используется строка, а дальше наша реализация выглядит следующим образом:

/**
 * @author felord.cn
 * @since 17:57
 **/
public class ColorConstraintValidator implements ConstraintValidator<Color, String> {
    private static final Set<String> COLOR_CONSTRAINTS = new HashSet<>();

    @Override
    public void initialize(Color constraintAnnotation) {
        Colors[] value = constraintAnnotation.value();
        List<String> list = Arrays.stream(value)
                .map(Enum::name)
                .collect(Collectors.toList());
        COLOR_CONSTRAINTS.addAll(list);

    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return COLOR_CONSTRAINTS.contains(value);
    }
}

Затем объявите соответствующую аннотацию ограниченияColor, для которого требуется мета-аннотация@ConstraintУказывает на использование класса обработки, определенного выше.ColorConstraintValidatorПроверьте это.

/**
 * @author felord.cn
 * @since 17:55
 **/
@Constraint(validatedBy = ColorConstraintValidator.class)
@Documented
@Target({ElementType.METHOD, ElementType.FIELD,
        ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR,
        ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Color {
    // 错误提示信息
    String message() default "颜色不符合规格";

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

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

    // 约束的类型
    Colors[] value();
}

Тогда давайте попробуем, сначала ограничим параметры:

@Data
public class Param {
    @Color({Colors.BLUE,Colors.YELLOW})
   private String color;
}

Как и несколько интерфейсов выше, вызов следующего интерфейса вызоветBindExceptionаномальный:

GET /student/color?color=CAY HTTP/1.1
Host: localhost:8888

Когда мы ставим параметрcolorназначить какBLUEилиYELLOWПосле этого ответ может быть успешно получен.

4.2 Часто задаваемые вопросы

Мы столкнемся с некоторыми проблемами в реальном использовании. Вот некоторые распространенные проблемы и решения.

Проверьте проблему, что базовый тип не действует

Выше, чтобы проверить цвет, мы объявляемParamобъект для переноса уникального строкового параметраcolor, зачем использовать следующее определение напрямую?

@GetMapping("/color")
public Rest<?> color(@Valid @Color({Colors.BLUE,Colors.YELLOW}) String color) {
    return RestBody.okData(color);
}

Или используйте переменную пути:

@GetMapping("/rest/{color}")
public Rest<?> rest(@Valid @Color({Colors.BLUE, Colors.YELLOW}) @PathVariable String color) {
    return RestBody.okData(color);
}

Вышеуказанные два метода не будут работать. Если вы мне не верите, вы можете попробовать, по крайней мере, вSpring Boot 2.3.1.RELEASEне вступит в силу напрямую.

Чтобы заставить два вышеупомянутых эффекта действовать, нужно добавить в класс@Validatedаннотация.Обратите внимание, что он должен быть добавлен в класс, в котором находится метод.. Это броситConstraintViolationExceptionаномальный.

Проблема в том, что элемент в параметре типа коллекции не действует

Как и следующее написание, когда параметр метода представляет собой коллекцию, как проверить ограничения элементов?

/**
 * 集合类型参数元素.
 *
 * @param student the student
 * @return the rest
 */
@PostMapping("/batchadd")
public Rest<?> batchAddStudent(@Valid @RequestBody List<Student> student) {
    return RestBody.okData(student);
}

То же самое добавляется в класс@Validatedаннотация.Обратите внимание, что он должен быть добавлен в класс, в котором находится метод.. Это броситConstraintViolationExceptionаномальный.

Вложенная проверка не действует

Как проверить вложенную структуру? Например, если мы в студенческом классеStudentДобавлена ​​информация о школеSchoolи надеюсьSchoolсвойства проверяются.

@Data
public class Student {

    @NotBlank(message = "姓名必须填")
    private String name;
    @Age
    private Integer age;
    @NotEmpty(message = "成绩必填")
    private List<Double> scores;
    @NotNull(message = "学校不能为空")
    private School school;
}


@Data
public class School {
    @NotBlank(message = "学校名称不能为空")
    private String name;
    @Min(value = 0,message ="校龄大于0" )
    private Integer age;
}

когдаGETПроверяется нормально при запросеSchoolсвойства, ноPOSTЗапросить, но не могуSchoolсвойства проверяются. В настоящее время нам нужно только добавить его в этот атрибут.@ValidПросто аннотируйте.

@Data
public class Student {

    @NotBlank(message = "姓名必须填")
    private String name;
    @Age
    private Integer age;
    @NotEmpty(message = "成绩必填")
    private List<Double> scores;
    @Valid
    @NotNull(message = "学校不能为空")
    private School school;
}

Каждый дополнительный слой вложенности требует дополнительного слоя@Validаннотация. Обычно при проверке свойств объекта@NotNull,@NotEmptyа также@ValidСотрудничество может служить кассой.

Если у вас есть другие вопросы, вы можете связаться со мной через felord.cn.

5. Резюме

Проверяя структуру, мы можем сосредоточиться на развитии бизнеса.Hibernate ValidatorИспользование и некоторые распространенные проблемы разобраны. мы можем пройтиSpring BootУнифицированная обработка исключений для решения проблемы запроса ненормальной информации при проверке параметров. В частности, вы можете обратить внимание на:Код Фермер Маленький Толстый БратОтвечатьvalidДоступ к соответствующимDEMO.