如何 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
。那是:
- 默认no-arg构造函数
- 另一个用
@JsonCreator
注释的构造函数
- 用
@JsonCreator
注释的静态工厂方法
如果我们看一下 spring 对 Page
- PageImpl
和 GeoPage
的实现,它们都没有。所以你有两个选择:
- 编写自定义解串器并将其注册到
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);
- 制作自己的 类 扩展
PageImpl
、PageRequest
等,并用 @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");
}
就我个人而言,我会选择第二个选项,即使您必须创建更多 类,它也免除了在编写反序列化程序时手动提取属性和创建实例的繁琐。
这似乎是 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
。那是:
- 默认no-arg构造函数
- 另一个用
@JsonCreator
注释的构造函数 - 用
@JsonCreator
注释的静态工厂方法
如果我们看一下 spring 对 Page
- PageImpl
和 GeoPage
的实现,它们都没有。所以你有两个选择:
- 编写自定义解串器并将其注册到
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);
- 制作自己的 类 扩展
PageImpl
、PageRequest
等,并用@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");
}
就我个人而言,我会选择第二个选项,即使您必须创建更多 类,它也免除了在编写反序列化程序时手动提取属性和创建实例的繁琐。