簡體   English   中英

如何使反應式@WebFilter 與@RequestBody 一起正常工作?

[英]How to make reactive @WebFilter work properly with @RequestBody?

我正在嘗試制作一個反應式@WebFilter ,它在實際服務器交換之前之后執行東西(即處理請求的 controller 代碼):

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

但是,當 controller 收到一個注釋為@RequestBody的參數時,會發生一些真正意想不到的事情。 比如說,一個從客戶端獲取Mono<String>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");
    }
}

在這種情況下,我的過濾器中的所有步驟都在 controller 完成請求之前執行。 這意味着 web 交換獨立於過濾器提交,因此在將響應發送回客戶端后我無法立即執行任何操作。

所以我想知道:

  • 這是某種 Spring 錯誤嗎?
  • 難道我做錯了什么?
  • 或者這只是預期的行為?

我創建了一個包含重現問題的測試用例的小要點:


在布賴恩的評論后編輯:

我仍然認為這可能是一個錯誤,因為Mono.then似乎根本沒有任何效果:

        @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()));
        }

我認為這不是 Spring 中的錯誤(在這種情況下也不是 Reactor 中的錯誤),而是錯誤地選擇了運算符來實現您想要做的事情。

  • Mono.doOnEach對每個信號執行(下一個元素,完成,錯誤,取消); 在我們的例子中,每個請求都會執行幾次。
  • Mono.and加入終止信號 - 因此它等待Mono完成然后完成。 但是兩個 Mono 都不是按順序執行的,它們是同時訂閱的。 Mono.just完成,與過濾器鏈發生的情況無關。

在您的情況下,您不需要比在處理開始時添加一個運算符( doOnSubscribe在映射完成並且我們開始執行處理程序時發生)和我們完成時添加另一個更復雜的操作( doFinally在完成時發生,包括完成、錯誤或取消)。

    @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方法中執行阻塞操作,這種方法就可以工作,因為它們是副作用方法。 這樣做會在您的應用程序中產生嚴重的性能問題(並且反應器甚至可能會拒絕該操作並出現異常)。 因此,記錄、向 map 添加元素是可以的——但例如,將某些內容發布到事件隊列就不行。 在這種情況下,您應該使用Mono.then()代替鏈操作。

編輯

我認為Mono.then()沒有錯誤 - doOnEach僅適用於向下游發送的信號(下一個,錯誤,完成)。 在這種情況下,您應該只獲得完整的信號。 如果您想在所有情況下獲取上下文,可以查看Mono.deferWithContext

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM