当前位置: 首页 > 知识库问答 >
问题:

如何使反应式@WebFilter与@Request estbody一起正常工作?

任元青
2023-03-14

我正在尝试制作一个反应式@WebFilter,它在实际服务器交换之前和之后执行内容(即处理请求的控制器代码):

public static class Foobar implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        return Mono.empty()
                .doOnEach(s -> /* BEFORE */))
                .then(chain.filter(exchange) /* CONTROLLER */)
                .and(Mono.empty().doOnEach(s -> /* AFTER */))
                .and(Mono.empty().doFinally(s -> /* FINALLY */));
    }
}

对于返回Mono的简单GET请求,一切都按预期工作:

@RestController
@RequestMapping
public static class Foo {

    @GetMapping
    @PostMapping(value = "foo")
    public Mono<String> foo(ServerWebExchange exchange) {
        return Mono.just("FOOBAR").map(e -> "OK");
    }
}

但是当控制器收到一个注释为@Request estbody的参数时,会发生一些真正意想不到的事情。例如,一个接受Mono的POST请求

@RestController
@RequestMapping
public static class Bar {

    @PostMapping(value = "bar")
    public Mono<String> bar(ServerWebExchange exchange, @RequestBody Mono<String> text) {
        return text.map(s -> "OK");
    }
}

在这种情况下,我的过滤器中的所有步骤都在控制器完成请求之前执行。这意味着Web交换是独立于过滤器提交的,因此我无法在响应发送回客户端后正确执行任何操作。

所以我想知道:

  • 这是Spring的虫子吗

我创建了一个小要点,其中包含一个再现问题的测试用例:

  • https://gist.github.com/guillermocalvo/740b4fcab471ebc6fe69227fee6d79d5

在Brian的评论后编辑:

我仍然认为这可能是一个bug,因为Mono。那么看来根本没有任何效果:

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
            return chain.filter(exchange)
                .doOnSubscribe(s -> logger.info("onSubscribe response committed:" +
                        exchange.getResponse().isCommitted()))
                .then().doOnEach(s -> logger.info("then doOnEach response committed:" +
                        exchange.getResponse().isCommitted()))
                .doFinally(s -> logger.info("doFinally response committed:" +
                        exchange.getResponse().isCommitted()));
        }

此外,如果我把东西放在doOnEach中,也不会执行:

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
            return chain.filter(exchange)
                    .doOnSubscribe(s -> logger.info("FILTER-BEFORE-CHAIN/commited=" +
                        response.isCommitted()))
                    .doOnEach(s -> logger.info("FILTER-AFTER-CHAIN/commited=" +
                        response.isCommitted()))
                    .doFinally(s -> logger.info("FILTER-FINALLY/commited=" +
                        response.isCommitted()));
        }

共有1个答案

顾文昌
2023-03-14
匿名用户

我不认为这是Spring中的错误(在这种情况下也不是Retor中的错误),而是错误地选择了运算符来实现您正在尝试做的事情。

  • Mono.doOneach为每个信号执行(下一个元素、完成、错误、取消);在我们的例子中,每个请求都会执行几次。
  • Mono.and加入终止信号-因此它等待两个Mono完成,然后完成。但是两个Monos不是按顺序执行的,它们是同时订阅的。Mono.just立即完成,与过滤器链发生的情况无关。

在您的情况下,不需要比在处理开始时添加一个操作符(映射完成并且我们开始执行处理程序时发生doOnSubscribe)和完成时添加另一个操作符(完成、错误或取消)更复杂的操作。

    @Component
    public class MyFilter implements WebFilter {

        Logger logger = LoggerFactory.getLogger(getClass());

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
            return chain.filter(exchange)
                .doOnSubscribe(s -> logger.info("onSubscribe response committed:" +
                        exchange.getResponse().isCommitted()))
                .doFinally(s -> logger.info("doFinally response committed:" + 
                        exchange.getResponse().isCommitted()));
        }
    }

请注意,只要不在DoOnXYZ方法中执行阻塞操作,这种方法就可以工作,因为它们是副作用方法。这样做会在应用程序中造成严重的性能问题(reactor甚至可能会异常拒绝该操作)。因此,记录日志、向映射中添加元素是可以的,但将某些内容发布到事件队列则不行。在这种情况下,您应该将链式操作改为与Mono一起使用。然后()。

我认为Mono没有bug。then()-doOnEach只对发送到下游的信号起作用(next,error,complete)。在这种情况下,您应该只获得完整的信号。如果您想了解所有情况下的上下文,可以查看Mono。延迟上下文

 类似资料:
  • 我对反应式编程有点陌生,我正在尝试组装以下组件:使用Java、Springboot 2、Webflux和reactor core,我想处理需要额外身份验证的非常特定的请求。因此,我通过一系列步骤实现了一个Web过滤器: 捕获请求的路径和方法。检查该组合是否存在,是否需要使用accessPointService进行特定身份验证。getAccessPointAuthorizationRequireme

  • 问题内容: 如何防止过滤器剥离非ASCII字母数字字符?(我正在使用Django 1.0.2) cnprog.com的网址中包含汉字,因此我查看了他们的代码。他们没有在模板中使用,而是在模型中调用此方法来获取永久链接 他们是不是在撒谎URL? 问题答案: 我在Askbot问答论坛上采用了一个名为unidecode的python软件包,它适用于基于拉丁语的字母,甚至对希腊人来说也很合理: 它对亚洲语

  • 我对scrollview和GeometryReader有一些问题。我想要一张图片下的物品清单。每个项目都应该有以下宽度和高度: 我为我的用例尝试了两种方法。这是我的第一个代码结构: 我正在使用几何体读取器来获取VStack的宽度,因为它有一个填充,我不想获得滚动视图的全宽。 但对于GeometryReader,UI上只显示ForEach循环中的最后一项。而GeometryReader只有很小的高度

  • 我正在尝试解决这个问题:如何通过REST控制器用Spring(引导)重写URL?通过创建某种类型的“过滤器”来应用于每个传入的HTTP请求。 这个问题包含了一些答案,比如这个问题:Spring Boot添加了Http请求拦截器,但接口处理的是Javax'和,它们不如Spring引入的新类实用,即(请参阅下面代码中)出现在一个名称听起来很有前途的接口: 所以我的结尾是这样的: ...就像人们在做类似

  • 问题内容: 在我编写的flask应用程序中,我使用了一个外部库,该库可以使用环境变量进行配置。注意:我自己编写了这个外部库。因此,如有必要,我可以进行更改。从命令行运行时,运行带有以下内容的烧瓶服务器: 一切都如预期。但将它部署到Apache后,用它不工作了。事实上,打印出到(所以它在Apache日志中显示出来显示,该wsgi过程似乎是在一个非常不同的环境(一个,好像是这样了。其实,它指向我的发展

  • 我正在尝试使用Micrometer从我的spring boot应用程序中公开度量。 以下配置由于某种原因不起作用: spring说: 但是对于完全相同的注释配置,它可以很好地工作: 日志说: 完整的示例如下:https://filebin.net/gjxnwwksaqs1x8zg