首先先給出mini版專案
@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類為資料類,裡面有一個list存放String,業務邏輯就是將User類中的String由小寫轉為大寫,返回給前臺,很簡單吧。
之前的業務程式碼裡只有strings不為空,且長度大於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;
複製程式碼
這樣的話,我們拿到的list就是已經過濾掉null物件的list了。這個時候在對整個集合進行非空校驗和長度校驗,就可以確保沒問題。
但是講道理的話,前臺傳這種資料過來,多半是他們自己的邏輯出現了問題,我們就這樣把問題吃了也不是特別合適。所以得通過校驗告訴他們哪裡有問題。
自定義校驗器
在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容器中的類。這個功能也很有用。