Сначала дайте мини версию проекта
@Data
public class User {
@NotNull
@Size(min = 1)
private List<String> strings;
}
@RequestMapping("")
public User hello(@Validated @RequestBody User user) {
user.setStrings(user.getStrings()
.stream()
.map(String::toUpperCase)
.collect(Collectors.toList()));
return user;
}
Класс User — это класс данных, и в нем есть список для хранения String. Бизнес-логика состоит в том, чтобы преобразовать String в классе User из нижнего регистра в верхний и вернуть его на стойку регистрации. Это очень просто.
В предыдущем бизнес-коде только непустые строки, длина которых больше 1, могут пройти проверку.
Но надежно ли это? Я тоже сначала так думал, пока однажды на стойке регистрации не прислали такой объект
"strings":["abc",null,"def"]
Затем я сообщил об исключении NullPoint здесь. Так вот это место тоже пропустило проверку, то есть объекты в коллекции не могут быть пустыми!
Что делать, если объекты в коллекции не могут быть пустыми? Если вы не хотите помещать эту часть проверки в бизнес-код, есть два решения:
1.在转换的时候,过滤掉null。
2.在校验的时候,校验集合里的对象为非空。
1. Фильтр при конвертации
Прежде всего, что мне делать, если мне нужно фильтровать при конвертации? Перейдите непосредственно к коду:
@Configuration
public class GlobalConfiguration {
@Bean
//向spring容器中注入fastjson消息转换器(我比较喜欢用这个,你们随意)
public HttpMessageConverters message(){
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
FastJsonHttpMessageConverter fastJson = new FastJsonHttpMessageConverter();
fastJson.setFastJsonConfig(fastJsonConfig);
return new HttpMessageConverters(fastJson);
}
}
//自定义fastjson反序列化组件
public class ListNotNullDeserializer implements ObjectDeserializer {
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
if (parser.lexer.token() == JSONToken.NULL) {
parser.lexer.nextToken(JSONToken.COMMA);
return null;
}
Collection list = TypeUtils.createCollection(type);
Type itemType = TypeUtils.getCollectionItemType(type);
parser.parseArray(itemType, list, fieldName);
//在返回的时候,过滤掉集合里的null对象
return (T) list.stream().filter(Objects::nonNull).collect(Collectors.toList());
}
@Override
public int getFastMatchToken() {
return 0;
}
}
//在需要过滤的集合上面添加相应的转换器标志
@JSONField(deserializeUsing = ListNotNullDeserializer.class)
private List<String> strings;
В этом случае список, который мы получаем, представляет собой список с отфильтрованными нулевыми объектами. В это время для всего набора выполняется непустая проверка и проверка длины, чтобы убедиться в отсутствии проблем.
Но если быть разумным, если на стойке регистрации передаются такие данные, у большинства из них проблемы с собственной логикой, и нам не особенно подходит такая проблема. Таким образом, они должны пройти проверку, чтобы сказать им, что не так.
пользовательский валидатор
В springboot я не нашел аннотации, которая может проверить, что все объекты в коллекции не пусты. Так что похоже, что я должен реализовать один сам.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Inherited
@Documented
@Constraint(validatedBy = NotNullEleValidator.class)
public @interface NotNullElement {
String message() default " 集合中有元素为null !";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class NotNullEleValidator implements ConstraintValidator<NotNullElement, List<?>> {
@Override
public boolean isValid(List<?> value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
for (Object o : value) {
if (o == null) {
return false;
}
}
return true;
}
}
Пользовательское правило валидатора также очень просто проверить, возвращает ли оно true, если оно не возвращает false, так что Springboot может получить информацию о проверке, чтобы решить, следует ли генерировать исключение.
Следует отметить, что загрузка валидатора завершается фреймворком spring, то есть валидатор может использовать классы в контейнере spring. Эта функция также полезна.