在编写了几个后端API后,我发现以下代码几乎在每个需要按日期过滤数据的方法中都重复:
@GetMapping(value="/api/test")
@ResponseBody
public Result test(@RequestParam(value = "since", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate since,
@RequestParam(value = "until", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate until) {
// Date validation code I want to eliminate
since = ObjectUtils.defaultIfNull(since, DEFAULT_SINCE_DATE);
until = ObjectUtils.defaultIfNull(until, LocalDate.now().plusDays(1));
if(since.isAfter(until)) {
throw new SinceDateAfterUntilDateException();
}
// Do stuff
}
显然这是某种代码味道。但是,因为我确实需要在使用它们来查询服务/ DAO之前验证since
和until
,我不知道应该在哪里提取这些代码?
有什么建议吗?
答案 0 :(得分:0)
public class MyCustomDateTypeConverter implements Converter<String, LocalDate> { @Override public LocalDate convert(String param) { //convert string to DateTime return dateTiemObjectCreatedfromParam; } }
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <list>
<bean class="com.x.y.z.web.converters.MyCustomDateTypeConverter"/> </list> </property>
</bean>
<mvc:annotation-driven conversion-service="conversionService">
</mvc:annotation-driven>
public Result test(LocalDate since,LocalDate until) {
since = ObjectUtils.defaultIfNull(since, DEFAULT_SINCE_DATE);
until = ObjectUtils.defaultIfNull(until, LocalDate.now().plusDays(1));
if(since.isAfter(until)) {
throw new SinceDateAfterUntilDateException();
}
// Do stuff
}
答案 1 :(得分:0)
正如ol'gud-procedural方法所暗示的那样:
public Result test(@RequestParam(value = "since", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate since,
@RequestParam(value = "until", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate until) {
checkInputDates();
// Do stuff
}
private checkInputDates(LocalDate since, LocalDate until) {
since = ObjectUtils.defaultIfNull(since, DEFAULT_SINCE_DATE);
until = ObjectUtils.defaultIfNull(until, LocalDate.now().plusDays(1));
if(since.isAfter(until)) {
throw new SinceDateAfterUntilDateException();
}
}
//so on..
答案 2 :(得分:0)
如果您有从对象收到的请求参数,那么您可以通过使用Bean级别验证(JSR 303)和自定义日期反序列化器来扩展Jackson序列化程序。这样你就没有检查params为null。
{{1}}
答案 3 :(得分:0)
我建议使用自定义 bean validation 保存参数 since
和 until
的模型类型(使用 Lombok,但您也可以编写 getter 和 setter)。默认值现在是字段初始值设定项:
@Ordered({"since", "until"})
@Data
public class DateRange {
@NotNull
@PastOrPresent
private LocalDate since = DEFAULT_SINCE_DATE;
@NotNull
private LocalDate until = LocalDate.now().plusDays(1);
}
@GetMapping(value="/api/test")
@ResponseBody
public Result test(@Valid DateRange dateFilter) {
// Do stuff
}
要进行验证,您需要自定义 bean 验证约束:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = OrderedValidator.class)
public @interface Ordered {
/** The property names with comparable values in the expected order. **/
String[] value();
String message() default "{com.stackoverflow.validation.Ordered.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
以及检查约束的验证器(抱歉,小泛型让它适用于任何类型的 Comparable
值,而不是仅适用于 LocaleDate
):
public class OrderedValidator implements ConstraintValidator<Ordered, Object>
{
private String[] properties;
@Override
public void initialize(Ordered constraintAnnotation) {
if (constraintAnnotation.value().length < 2) {
throw new IllegalArgumentException("at least two properties needed to define an order");
}
properties = constraintAnnotation.value();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
return isValid(value));
}
private <T extends Comparable<? super T>> boolean isValid(Object value)
{
List<T> values = getProperties(value);
return isSorted(values);
}
private <T extends Comparable<? super T>> List<T> getProperties(Object value)
{
BeanWrapperImpl bean = new BeanWrapperImpl(value);
return Stream.of(properties)
.map(bean::getPropertyDescriptor)
.map(pd -> this.<T>getProperty(pd, value))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
// See https://stackoverflow.com/a/3047160/12890
private <T extends Comparable<? super T>> boolean isSorted(Iterable<T> iterable) {
Iterator<T> iter = iterable.iterator();
if (!iter.hasNext()) {
return true;
}
T t = iter.next();
while (iter.hasNext()) {
T t2 = iter.next();
if (t.compareTo(t2) > 0) {
return false;
}
t = t2;
}
return true;
}
@SuppressWarnings("unchecked")
private <T extends Comparable<? super T>> T getProperty(PropertyDescriptor prop, Object bean) {
try {
return prop.getReadMethod() == null ? null : (T)prop.getReadMethod().invoke(bean);
} catch (ReflectiveOperationException noAccess) {
return null;
}
}
}