首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Flux的Spring Cloud Gateway modifyResponseBody

Flux的Spring Cloud Gateway modifyResponseBody
EN

Stack Overflow用户
提问于 2020-06-23 01:38:16
回答 2查看 963关注 0票数 0

我在修改Spring Cloud Gateway中的Flux响应时遇到了一些问题。我的设置(简化)如下:我有2个域对象:

代码语言:javascript
复制
@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作为响应。

我的路线如下:

代码语言:javascript
复制
@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中得到了这个异常:

代码语言:javascript
复制
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对我来说并不清楚。谁能给我指个方向?谢谢!

EN

回答 2

Stack Overflow用户

发布于 2020-06-23 13:33:13

从spring cloud gateway的源代码中,你会发现一个TODO,上面写着'TODO: flux or mono‘。这意味着目前只支持mono类型。

代码语言:javascript
复制
// 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,也许你可以把类放在一个包装类中,例如

代码语言:javascript
复制
class PersonV2ListWrapper{
    public List<PersonV2> persons;
}

另外,使用self filter来实现ModifyResponseBodyFilter的功能并支持Flux也会很有帮助。

票数 1
EN

Stack Overflow用户

发布于 2020-06-24 00:12:20

多亏了@eric的提示,我才能让它工作起来。一些警告:现在将集合包装在Mono中,当然,我现在正在消除反应式流的一些好处,所以更好的方法当然是编写一个支持Flux的ModifyResponseBodyFilter实现。另一个项目稍微偏离了原始问题的主题,所以我将在这篇文章的末尾介绍。

最终对我有效的解决方案是:

代码语言:javascript
复制
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。以下是测试结果:

代码语言:javascript
复制
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路由放在第一位,那么个人请求的完整列表就会失败。在我看来,额外的谓词不应该是必要的,但是为了让它工作,我不得不添加这些。

任何关于这个主题的额外评论/帮助都是非常受欢迎的,因为即使这个“工作”,解决方案仍然不是理想的。

希望这至少能帮助到一些人。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62520341

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档