如何将Vaadin网格与Spring Mono WebClient数据的结果链接起来



这似乎是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的经验,所以我将讨论反序列化问题。

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

  1. 默认的无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注释参数

您的页面:

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");
}

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

这个问题有两部分。

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

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

最新更新