![](/img/trans.png)
[英]What is the best practice for implementing common methods between spring rest controllers?
[英]Best practice for using date parameters in Spring controllers?
写了几个后端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
}
显然,这是某种代码异味。 但是,因为我确实需要验证since
和until
使用它们查询服务/ DAO,我不知道我应该在哪里提取这些代码来过吗?
有什么建议吗?
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
}
正如 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..
如果您从对象收到请求参数,那么您可以通过扩展 Jackson 序列化程序使用 Bean 级别验证(JSR 303)和自定义日期反序列化程序来完成。 这样你就不用检查参数是否为空。
public class yourBeanName{
public LocalDate since;
public LocalDate until;
@JsonDeserialize(using = CustomDateDeserializer.class)
public void setSince(LocalDate since) {
this.since = since;
}
// same for until
}
// Custom jackson Desealizer
@Component
public class CustomDateDeserializer extends StdDeserializer<Date> {
@Override
public Date deserialize(JsonParser jsonparser, DeserializationContext
context)
throws IOException, JsonProcessingException {
// Here check the date for null and assign default with your dateTimeFormat
}
}
我建议一个模型型保持的参数since
和until
有一个自定义的Bean验证(使用龙目岛,但你也可以写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;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.