如何 link 具有 Spring Mono WebClient 数据结果的 Vaadin 网格

How to link a Vaadin Grid with the result of Spring Mono WebClient data

这似乎是 Vaadin 文档中缺少的部分...

我调用 API 来获取我的 UI 中的数据,如下所示:

@Override
public URI getUri(String url, PageRequest page) {
    return UriComponentsBuilder.fromUriString(url)
            .queryParam("page", page.getPageNumber())
            .queryParam("size", page.getPageSize())
            .queryParam("sort", (page.getSort().isSorted() ? page.getSort() : ""))
            .build()
            .toUri();
}

@Override
public Mono<Page<SomeDto>> getDataByPage(PageRequest pageRequest) {
    return webClient.get()
            .uri(getUri(URL_API + "/page", pageRequest))
            .retrieve()
            .bodyToMono(new ParameterizedTypeReference<>() {
            });
}

在 Vaadin 文档 (https://vaadin.com/docs/v10/flow/binding-data/tutorial-flow-data-provider) 中,我找到了一个带有 DataProvider.fromCallbacks 的示例,但这需要流,并且感觉这不是正确的方法,因为我需要阻止对获取流...

DataProvider<SomeDto, Void> lazyProvider = DataProvider.fromCallbacks(
     q -> service.getData(PageRequest.of(q.getOffset(), q.getLimit())).block().stream(),
     q -> service.getDataCount().block().intValue()
);

尝试此实现时,出现以下错误:

org.springframework.core.codec.CodecException: Type definition error: [simple type, class org.springframework.data.domain.Page]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.data.domain.Page` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 1]
grid.setItems(lazyProvider);

这个问题分为两部分。

第一个是关于在 Vaadin 中为 DataProvider 异步加载数据。这不受支持,因为 Vaadin 已优先考虑直接通过 JDBC 获取数据的典型案例。这意味着您最终会在加载数据时阻塞线程。 Vaadin 23 will add support 用于在单独的线程上进行阻塞而不是保持 UI 线程阻塞,但它仍然会阻塞。

你的另一半问题似乎与 Vaadin 没有直接关系。异常消息表示 REST 客户端使用的 Jackson 实例未配置为支持创建 org.springframework.data.domain.Page 的实例。我对这部分问题没有直接经验,所以我无法就如何修复它给出任何建议。

我没有使用vaadin的经验,所以我会谈谈反序列化问题。

Jackson 在反序列化时需要 Creator。那是:

  1. 默认no-arg构造函数
  2. 另一个用@JsonCreator注释的构造函数
  3. @JsonCreator注释的静态工厂方法

如果我们看一下 spring 对 Page - PageImplGeoPage 的实现,它们都没有。所以你有两个选择:

  1. 编写自定义解串器并将其注册到 ObjectMapper 实例

解串器:

public class PageDeserializer<T> extends StdDeserializer<Page<T>> {

    public PageDeserializer() {
        super(Page.class);
    }

    @Override
    public Page<T> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
        //TODO implement for your case
        return null;
    }
}

及报名:

SimpleModule module = new SimpleModule();
module.addDeserializer(Page.class, new PageDeserializer<>());
objectMapper.registerModule(module);
  1. 制作自己的 类 扩展 PageImplPageRequest 等,并用 @JsonCreator 注释它们的构造函数,用 @JsonProperty.[=57 注释它们的参数=]

您的页面:

public class MyPage<T> extends PageImpl<T> {

    @JsonCreator
    public MyPage(@JsonProperty("content_prop_from_json") List<T> content, @JsonProperty("pageable_obj_from_json") MyPageable pageable, @JsonProperty("total_from_json") long total) {
        super(content, pageable, total);
    }
}

您的可分页:

public class MyPageable extends PageRequest {

    @JsonCreator
    public MyPageable(@JsonProperty("page_from_json") int page, @JsonProperty("size_from_json") int size, @JsonProperty("sort_object_from_json") Sort sort) {
        super(page, size, sort);
    }
}

根据您对 Sort 对象的需要,您可能还需要创建 MySort,或者您可以将其从构造函数中删除并提供未排序的排序,例如,给超级构造函数。如果您手动反序列化输入,则需要提供如下类型参数:

JavaType javaType = TypeFactory.defaultInstance().constructParametricType(MyPage.class, MyModel.class);
Page<MyModel> deserialized = objectMapper.readValue(pageString, javaType);

例如,如果输入来自请求正文,只需在变量中声明泛型类型就足以让对象映射器获取它。

@PostMapping("/deserialize")
public ResponseEntity<String> deserialize(@RequestBody MyPage<MyModel> page) {
    return ResponseEntity.ok("OK");
}

就我个人而言,我会选择第二个选项,即使您必须创建更多 类,它也免除了在编写反序列化程序时手动提取属性和创建实例的繁琐。