【问题标题】:Spring Boot converter ignored for nested property嵌套属性忽略 Spring Boot 转换器
【发布时间】:2020-07-18 16:16:06
【问题描述】:

我有一个TaskFilter 被用作@RestController 中的参数:

@GetMapping("/tasks")
public ResponseEntity<List<TaskDTO>> getAllTasks(Pageable pageable, TaskFilter filter)

如果我调用GET /api/tasks?priority=low 那么它工作正常,构造函数被调用。但是,如果我使用嵌套属性 GET /api/tasks?priority.gte=low 调用它会失败。如果嵌套属性是字符串,例如,没有问题。但是这里的 spring boots 似乎完全忽略了我的转换器。

如果映射(在另一条路由上)在根级别需要 TaskPriority,则它会毫无问题地转换,但当用作对象的属性时,转换不会发生并引发异常。

@Getter
@Setter
@NoArgsConstructor
@ToString
public class TaskPriorityFilter extends OrderedEnumFilter<TaskPriority> {

    @JsonCreator
    public TaskPriorityFilter(String tp) {
        super(TaskPriority.forValue(tp));
    }
}

@Getter
@Setter
@NoArgsConstructor
@ToString
abstract public class OrderedEnumFilter<E extends OrderedEnum> extends AbstractComparableFilter<E> {

    public OrderedEnumFilter(E eq) {
        super(eq);
    }

    public Object getValue() {
        if (null != eq) return eq.getOrder();
        if (null != gte) return gte.getOrder();
        if (null != gt) return gt.getOrder();
        if (null != lte) return lte.getOrder();
        if (null != lt) return lt.getOrder();
        if (null != between) return between.stream().map(OrderedEnum::getOrder).collect(Collectors.toList());

        return null;
    }
}
@Getter
@Setter
@ToString
abstract public class AbstractComparableFilter<T> implements PolymorphicFilter {

    static protected AtomicInteger identifierCount = new AtomicInteger(0);

    protected T eq;
    protected T gte;
    protected T gt;
    protected T lte;
    protected T lt;
    protected List<T> between;

    protected final int id;

    public void resetIfTooHigh() {
        identifierCount.compareAndSet(Integer.MAX_VALUE - 1, 0);
    }

    public AbstractComparableFilter() {
        this.id = identifierCount.getAndIncrement();
        resetIfTooHigh();
    }

    @JsonCreator
    public AbstractComparableFilter(T eq) {
        this.eq = eq;
        this.id = identifierCount.getAndIncrement();
        resetIfTooHigh();
    }

    public String getStartKey() {
        return "betweenStart" + id;
    }

    public String getEndKey() {
        return "betweenEnd" + id;
    }

    public Object getValue() {
        if (null != eq) return eq;
        if (null != gte) return gte;
        if (null != gt) return gt;
        if (null != lte) return lte;
        if (null != lt) return lt;
        if (null != between) return between;

        return null;
    }

    public ComparableType getFilterType() {
        if (null != eq) return ComparableType.EQ;
        if (null != gte) return ComparableType.GTE;
        if (null != gt) return ComparableType.GT;
        if (null != lte) return ComparableType.LTE;
        if (null != lt) return ComparableType.LT;
        if (null != between) return ComparableType.BETWEEN;

        throw new InvalidOperationException("unknown filter subtype");
    }
}
public enum TaskPriority {
    High(75),
    Medium(50),
    Low(25)
    ;

    private final Integer order;

    TaskPriority(Integer order) {
        this.order = order;
    }

    public Integer getOrder() {
        return order;
    }

    @JsonCreator
    public static TaskPriority forValue(String value) {
        return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, value));
    }

    @JsonCreator
    public static TaskPriority forValue(Integer order) {
        return Stream.of(TaskPriority.values())
            .filter(c -> order.equals(c.getOrder()))
            .findFirst()
            .orElseThrow(IllegalArgumentException::new);
    }

    @JsonValue
    public String toValue() {
        return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name());
    }
}

我已经声明了一个自定义转换器,该转换器已正确注册,但在转换过程中没有使用:

@Configuration
public class CustomConvertersConfiguration implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToTaskPriorityConverter());
    }
}

我也尝试添加@Component 注释,它并没有改变任何东西:

@Component
public class StringToTaskPriorityConverter implements Converter<String, TaskPriority> {
    @Override
    public TaskPriority convert(String source) {
        return null == source ? null : TaskPriority.forValue(source);
    }
}

编辑:我之前写了一个简化版本,通过这个编辑,这里显示的代码与我的代码完全相同。

另外,作为回顾:

  • GET /api/tasks?priority=low 结果为 200
  • GET /api/tasks?priority.gte=low 结果为 400

两者的结果都应该是 200。

【问题讨论】:

  • GET /api/tasks?priority.gte=low 工作没有任何错误。你的意思是说GET /api/tasks?priority.gte=25 不工作?
  • 我的意思是“priority.gte=low”不起作用。 'priority=low' 工作时。
  • 这很奇怪。我复制了您的代码来设置示例项目并且正在工作(我没有使用lombok,但我认为这没有任何区别)。我将创建一个 github 存储库
  • 我已经添加了 git repo。我的怀疑是你把 StringToTaskPriorityConverter 放在一个完全不同的包里,没有被扫描
  • 然而它正在被注册。当我使用调试器时,会触发断点。

标签: spring-boot type-conversion


【解决方案1】:
  • 以下构造函数导致StringToTaskPriorityConverter 被忽略。 (最初这个问题有TaskPriority 作为构造函数参数,这就是我的回购与你的问题有相反行为的原因)
    @JsonCreator
    public TaskPriorityFilter(String tp) {
       super(TaskPriority.forValue(tp));
    }
  • 将构造函数替换为
    @JsonCreator
    public TaskPriorityFilter(TaskPriority tp) {
       super(tp);
    }
  • 又添加了一个自定义转换器,如下所示。
   @Component
public class StringToTaskPriorityFilterConverter implements 
                            Converter<String, TaskPriorityFilter> {
   @Override
   public TaskPriorityFilter convert(String source) {

       System.out.println("StringToTaskPriorityFilterConverter 
                                        is called for " + source);
       if (source == null)
           return null;
       try {
           return new 
TaskPriorityFilter(TaskPriority.forValue(Integer.parseInt(source)));
       } catch (Exception e) {
           return new TaskPriorityFilter(TaskPriority.forValue(source));
       }
   }
}
  • 通过上述更改,?priority=low?priority.gte=low 都可以正常工作。

  • 但是我不确定带有字符串参数的构造函数导致自定义转换器被忽略的原因。

参考

允许以下 3 个请求的 Github 存储库。 https://github.com/kavi-kanap/stackoverflow-62970847

【讨论】:

  • 我试过你的 repo,priority.gte=low 有效,但 priority=low 无效。
  • 那么它与您在代码中看到的相反吗?因为我没有检查priority=low,因为您的问题中没有提到它
  • 您介意用有效的端点列表和您希望有效但不能用作要点的端点更新问题吗?
  • 我刚生完孩子,赏金到期前我会回复你的。
  • 别担心赏金,先享受和宝宝在一起的时光
猜你喜欢
  • 2015-10-14
  • 2023-03-23
  • 1970-01-01
  • 2021-10-30
  • 1970-01-01
  • 2018-05-04
  • 2018-10-21
  • 2017-09-22
  • 2016-06-26
相关资源
最近更新 更多