我在修改Spring Cloud Gateway中的Flux响应时遇到了一些问题。我的设置(简化)如下:我有2个域对象:
@Data
@AllArgsConstructor
public class PersonV10 {
@NotNull
private String name;
}
@Data
@AllArgsConstructor
public class PersonV20 {
@NotNull
private String firstName;
@NotNull
private String lastName;
}现在,我的服务正在返回PersonV20的实例,并且使用Spring Cloud Gateway,我想修改响应以成为一个PersonV10。在/person上,我得到了Flux作为响应,在/person/{id}上,我得到了Mono作为响应。
我的路线如下:
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_response_v20", r -> r.path("/person/**")
.filters(f -> f.modifyResponseBody(PersonV20.class, PersonV10.class,
(exchange, p) -> Mono.just(new PersonV10(p.getFirstName() + " " + p.getLastName()))))
.uri("https://localhost:8444"))
.build();
}当调用/person/{id}端点时,这可以很好地工作,并且我的响应可以很好地修改为PersonV10。然而,当我现在调用/person端点时,我得到了一个Flux,并且我在Spring Cloud Gateway中得到了这个异常:
org.springframework.core.codec.DecodingException: JSON decoding error: Cannot deserialize instance of `com.example.gateway.mutate_response_filter.PersonV20` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `com.example.gateway.mutate_response_filter.PersonV20` out of START_ARRAY token
at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 1]
at org.springframework.http.codec.json.AbstractJackson2Decoder.processException(AbstractJackson2Decoder.java:215) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]现在我猜这是有意义的,因为我得到了一个Flux作为响应,而在modifyResponseBody中我使用了一个单声道。但在这种情况下如何使用Flux对我来说并不清楚。谁能给我指个方向?谢谢!
发布于 2020-06-23 13:33:13
从spring cloud gateway的源代码中,你会发现一个TODO,上面写着'TODO: flux or mono‘。这意味着目前只支持mono类型。
// TODO: flux or mono
Mono modifiedBody = extractBody(exchange, clientResponse, inClass)
.flatMap(originalBody -> config.getRewriteFunction().apply(exchange,
originalBody))
.switchIfEmpty(Mono.defer(() -> (Mono) config.getRewriteFunction()
.apply(exchange, null)));如果你想使用默认的ModifyResponseBodyFilter,也许你可以把类放在一个包装类中,例如
class PersonV2ListWrapper{
public List<PersonV2> persons;
}另外,使用self filter来实现ModifyResponseBodyFilter的功能并支持Flux也会很有帮助。
发布于 2020-06-24 00:12:20
多亏了@eric的提示,我才能让它工作起来。一些警告:现在将集合包装在Mono中,当然,我现在正在消除反应式流的一些好处,所以更好的方法当然是编写一个支持Flux的ModifyResponseBodyFilter实现。另一个项目稍微偏离了原始问题的主题,所以我将在这篇文章的末尾介绍。
最终对我有效的解决方案是:
private static final String ROOT_PATTERN = "/person";
private static final String INDIVIDUAL_PATTERN = "/person/*";
private AntPathMatcher pathMatcher = new AntPathMatcher();
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_response_v20_flux", r -> r.path(ROOT_PATTERN)
.filters(f -> f.modifyResponseBody(PersonV20[].class, PersonV10[].class,
(exchange, s) -> Mono.just(
Stream.of(s)
.map(v20 -> new PersonV10(v20.getId(), v20.getFirstName() + " " + v20.getLastName()))
.toArray(PersonV10[]::new)
)))
.uri("https://localhost:8444")
.predicate(e -> pathMatcher.match(ROOT_PATTERN, e.getRequest().getPath().value()))
)
.route("rewrite_response_v20_mono", r -> r.path(INDIVIDUAL_PATTERN)
.filters(f -> f.rewritePath("/person/(?<ID>.*)", "/person/${ID}")
.modifyResponseBody(PersonV20.class, PersonV10.class,
(exchange, s) -> Mono.just(new PersonV10(s.getId(), s.getFirstName() + " " + s.getLastName()))))
.uri("https://localhost:8444")
.predicate(e -> pathMatcher.match(INDIVIDUAL_PATTERN, e.getRequest().getPath().value()))
)
.build();
}现在回到我上面提到的警告:让我感到奇怪的是,目前两个路径模式不足以区分两个不同的端点。我已经编写了一个测试,用我提供的模式演示了AntPathMatcher (如果检查JavaDoc,它是在幕后使用的)能够区分/person和/person/1。以下是测试结果:
public class PathMatcherTests {
private AntPathMatcher pathMatcher = new AntPathMatcher();
private static final String ROOT_ENDPOINT = "/person";
private static final String INDIVIDUAL_ENDPOINT = "/person/1";
@Test
public void shouldMatchRootButNotIndividual() {
String rootMatchPattern = "/person";
assertTrue(pathMatcher.match(rootMatchPattern, ROOT_ENDPOINT));
assertFalse(pathMatcher.match(rootMatchPattern, INDIVIDUAL_ENDPOINT));
}
@Test
public void shouldMatchIndividualButNotRoot() {
String individualMatchPattern = "/person/*";
assertFalse(pathMatcher.match(individualMatchPattern, ROOT_ENDPOINT));
assertTrue(pathMatcher.match(individualMatchPattern, INDIVIDUAL_ENDPOINT));
}
}但是,即使此测试成功,如果我不在最后添加额外的谓词来测试请求路径,它对我也不起作用。如果我把rewrite_response_v20_flux路由放在第一位,那么个人请求就会失败,并显示一个DecodingException,就像我最初的post一样,如果我把rewrite_response_v20_mono路由放在第一位,那么个人请求的完整列表就会失败。在我看来,额外的谓词不应该是必要的,但是为了让它工作,我不得不添加这些。
任何关于这个主题的额外评论/帮助都是非常受欢迎的,因为即使这个“工作”,解决方案仍然不是理想的。
希望这至少能帮助到一些人。
https://stackoverflow.com/questions/62520341
复制相似问题