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.