Spring Boot использует JSR303 для реализации проверки параметров.

Spring Boot

Введение

JSR-303 — это подспецификация JAVA EE 6, называемая проверкой компонентов.

Всякий раз, когда вы имеете дело с бизнес-логикой приложения, проверка данных — это то, что вы должны учитывать и сталкиваться с этим. Приложение должно иметь средства для обеспечения семантической корректности входящих данных. В обычном случае приложение является многоуровневым, и разные слои создаются разными разработчиками. Много раз одна и та же логика проверки данных будет появляться на разных уровнях, что приведет к избыточности кода и некоторым проблемам управления, таким как семантическая согласованность. Чтобы избежать этой ситуации, лучше всего привязать логику проверки к соответствующей модели предметной области.

Bean Validation определяет соответствующую модель метаданных и API для проверки JavaBean. Метаданные по умолчанию — это аннотации Java, а исходная информация метаданных может быть перезаписана и расширена с помощью XML. В приложении вы можете убедиться в правильности модели данных (JavaBean) с помощью Bean Validation или собственных определенных ограничений, таких как @NotNull, @Max, @ZipCode. Ограничение может быть прикреплено к полю, методу получения, классу или интерфейсу. Для некоторых конкретных требований пользователи могут легко разработать собственные ограничения. Bean Validation — это среда проверки данных во время выполнения, и сообщения об ошибках проверки возвращаются сразу после проверки.

Аннотации ограничений, встроенные в спецификацию Bean Validation

пример

Базовое приложение

импортировать зависимости

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

Добавьте аннотации проверки к объектам параметров

@Data
public class User {
    
    private Integer id;
    @NotBlank(message = "用户名不能为空")
    private String username;
    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密码必须为8~16个字母和数字组合")
    private String password;
    @Email
    private String email;
    private Integer gender;

}

Добавьте @Valid перед параметром Bean, который необходимо проверить в контроллере, чтобы активировать функцию проверки, и добавьте BindingResult сразу после проверяемого Bean.BindingResult инкапсулирует результат проверки предыдущего Bean.

@RestController
@RequestMapping("/user")
public class UserController {

    @PostMapping("")
    public Result save (@Valid User user , BindingResult bindingResult)  {
        if (bindingResult.hasErrors()) {
            Map<String , String> map = new HashMap<>();
            bindingResult.getFieldErrors().forEach( (item) -> {
                String message = item.getDefaultMessage();
                String field = item.getField();
                map.put( field , message );
            } );
            return Result.build( 400 , "非法参数 !" , map);
        }
        return Result.ok();
    }

}

Тест выглядит следующим образом:

Унифицированная обработка исключений

В случае сбоя проверки параметра будет создано исключение BingBindException, которое может быть обработано единообразно в унифицированной обработке исключений, поэтому вам не нужно использовать BindingResult для получения результата проверки в каждом месте, где требуется проверка параметра.

@Slf4j
@RestControllerAdvice(basePackages = "com.itwolfed.controller")
public class GlobalExceptionControllerAdvice {

    @ExceptionHandler(value= {MethodArgumentNotValidException.class , BindException.class})
    public Result handleVaildException(Exception e){
        BindingResult bindingResult = null;
        if (e instanceof MethodArgumentNotValidException) {
            bindingResult = ((MethodArgumentNotValidException)e).getBindingResult();
        } else if (e instanceof BindException) {
            bindingResult = ((BindException)e).getBindingResult();
        }
        Map<String,String> errorMap = new HashMap<>(16);
        bindingResult.getFieldErrors().forEach((fieldError)->
                errorMap.put(fieldError.getField(),fieldError.getDefaultMessage())
        );
        return Result.build(400 , "非法参数 !" , errorMap);
    }

}

проверка разрешения группы

Правила проверки для новых сущностей и модификаций разные, например, когда id автоинкрементируется, id должен быть пустым при добавлении и изменении Entity, тогда нужно использовать групповую проверку.

Аннотации проверки имеют атрибут groups, который может группировать аннотации проверки Давайте посмотрим на исходный код @NotNull:

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
@Documented
@Constraint(validatedBy = { })
public @interface NotNull {

	String message() default "{javax.validation.constraints.NotNull.message}";

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

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

	@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
	@Retention(RUNTIME)
	@Documented
	@interface List {

		NotNull[] value();
	}
}

Из исходного кода видно, что groups — это массив типа Class>, тогда можно создать файл Groups.

public class Groups {
    public interface Add{}
    public interface  Update{}
}

Добавить группировку в аннотацию проверки объекта параметра

@Data
public class User {

    @Null(message = "新增不需要指定id" , groups = Groups.Add.class)
    @NotNull(message = "修改需要指定id" , groups = Groups.Update.class)
    private Integer id;
    @NotBlank(message = "用户名不能为空")
    @NotNull
    private String username;
    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密码必须为8~16个字母和数字组合")
    private String password;
    @Email
    private String email;
    private Integer gender;

}

Исходный @Valid в контроллере не может указывать группу и должен быть заменен на @Validated.

@RestController
@RequestMapping("/user")
public class UserController {

    @PostMapping("")
    public Result save (@Validated(Groups.Add.class) User user)  {
        return Result.ok();
    }

}

Тест выглядит следующим образом:

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

Хотя JSR303 и springboot-validator предоставили множество аннотаций проверки, столкнувшись со сложной проверкой параметров, они все еще не могут удовлетворить наши требования.В настоящее время нам нужно настроить аннотации проверки.

Например, для пола у пользователя используйте 1, чтобы представлять мужчину и 2 для представления женщин. Мы определяем аннотацию проверки @ListValue, а указанные значения могут быть только 1 и 2.

Создание правил ограничения

@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface ListValue {
    String message() default "";

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

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

    int[] vals() default { };
}

Аннотация определяется с помощью ключевого слова @interface Свойства в этой аннотации объявляются как методы, такие как Стиль, как того требует спецификация Bean Validation API:

  • Атрибут сообщения, этот атрибут используется для определения шаблона сообщения по умолчанию, когда ограничение не может быть проверено через Используйте это свойство для вывода сообщений об ошибках.
  • Атрибут groups используется для указания, к какой группе (группам) проверки принадлежит это ограничение. Значением по умолчанию для этого должен быть массив типа Class>.
  • Свойство полезной нагрузки, которое пользователи Bean Validation API могут использовать для указания уровней серьезности ограничений. Это свойство не используется самим API.

В дополнение к этим трем обязательным свойствам (сообщение, группы и полезная нагрузка) мы добавляем Добавлен атрибут для указания требуемого значения.Имя vals этого атрибута более конкретно в определении аннотации В частности, если назначено только это свойство, то это имя свойства можно игнорировать при использовании этой аннотации.

Кроме того, мы также аннотировали эту аннотацию некоторыми метааннотациями (мета аннотации):

  • @Target ({Method, Field, Annotation_Type}): указывает, что эту аннотацию можно использовать в методах, полях или Аннотация заявляет.
  • @Retention(RUNTIME): указывает, что информация аннотации считывается посредством отражения во время выполнения.
  • @Constraint(validatedBy = ListValueConstraintValidator.class): указывает, какой валидатор (класс) использовать для проверки элементов с помощью этой аннотации.
  • @Documented: указывает, что эта аннотация будет добавлена ​​к операции javadoc класса, который использует аннотацию. в джавадоке.

Создайте валидатор ограничений

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {

    private Set<Integer> set = new HashSet<>();
    /**
     * 初始化方法
     */
    @Override
    public void initialize(ListValue constraintAnnotation) {

        int[] vals = constraintAnnotation.vals();
        for (int val : vals) {
            set.add(val);
        }

    }

    /**
     * 判断是否校验成功
     *
     * @param value 需要校验的值
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {

        return set.contains(value);
    }
}

ListValueConstraintValidator определяет два общих параметра: первый — это тип аннотации, которую обслуживает этот валидатор (ListValue в нашем случае), а второй — тип проверяемого элемента (т. е. ListValue в нашем случае — Integer).

Если обозначенное ограничение поддерживает несколько типов проверяемых элементов, то необходимо определить тип каждого из поддерживаемых для ConstraintValidator и зарегистрировать вызов ограничения.

Реализация этого валидатора очень распространена.Метод initialize() передает экземпляр типа аннотации для проверки. В примере мы используем этот экземпляр, чтобы получить значение его свойства vals и сохранить его как коллекцию Set для следующего шага. использовать.

isValid() - это место, где реализуется реальная логика проверки, исходя из того, что данный int является условием ограничения @ListValue. Это законно.

Используйте аннотацию @ListValue для объекта параметра.

@Data
public class User {

    @Null(message = "新增不需要指定id" , groups = Groups.Add.class)
    @NotNull(message = "修改需要指定id" , groups = Groups.Update.class)
    private Integer id;
    @NotBlank(message = "用户名不能为空")
    @NotNull
    private String username;
    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密码必须为8~16个字母和数字组合")
    private String password;
    @Email
    private String email;
    @ListValue( message = "性别应指定相应的值" , vals = {1,2} , groups = {Groups.Add.class , Groups.Update.class})
    private Integer gender;

}

Тест выглядит следующим образом:

Адрес источника

GitHub.com/Kung Fu-Change сенсорный экран…

Ссылаться на

Woohoo. IBM.com/developer Я… docs.JBoss.org/hibernate/V…

Подписывайтесь на меня