首页 > 解决方案 > 如何在 Spring 反应式处理程序函数中提取请求正文?

问题描述

我正在使用 Spring Web Flux 实现处理程序函数,并希望提取x-www-form-urlencodedPOST-Body 的值。

public Mono<ServerResponse> handle(ServerRequest incomingHttpRequest) {
    Mono<MultiValueMap<String, String>> formData = incomingHttpRequest.body(BodyExtractors.toFormData());
    return formData
            .doOnEach(signal -> {LOG.info("EACH: " + signal.getType());})
            .doOnSubscribe(x -> {LOG.info("SUBSCRIBED");})
            .doOnNext(next -> {LOG.info("NEXT: " + next);})
            .doOnError(x -> {LOG.info("ERROR: " + x.getMessage());})
            .doOnSuccess(x -> {LOG.info("SUCCESS: " + x);})
            .flatMap(multiValueMap -> ServerResponse
                .badRequest()
                .body(BodyInserters.fromObject(
                    "searchTerm: " + multiValueMap.getFirst("searchTerm")
                ))
            );
}

处理程序被添加到路由器,如:

return RouterFunctions.route(RequestPredicates.POST("/mysearch"), searchHandler::handle);

通过 Postman 发送带有 Body searchTerm=something&lang=de 的 POST 会得到以下结果:

o.s.w.s.adapter.HttpWebHandlerAdapter    : [f405c02b] HTTP POST "/mysearch"                                                                               
o.s.http.codec.FormHttpMessageReader     : [f405c02b] Read form fields [searchTerm, lang] (content masked)                                                
o.s.w.r.f.s.s.RouterFunctionMapping      : [f405c02b] Mapped to de.myproject...
de.myproject.MyHandler                   : SUBSCRIBED                                                                                                     
de.myproject.MyHandler                   : EACH: onComplete                                                                                               
de.myproject.MyHandler                   : SUCCESS: null                                                                                                  
o.s.w.s.adapter.HttpWebHandlerAdapter    : [f405c02b] Completed 200 OK                                                                                    

我希望得到一个包含 searchTerm 的 BAD_REQUEST。但是我得到了200。

对我来说,看起来生成的 Mono 已被订阅,然后成功完成。但formData永远不会收到值,因此永远不会进入flatMap(...)

我在做错什么映射Mono<MultiValueMap<String, String>>到 a Mono<ServerResponse>

标签: javaproject-reactor

解决方案


这里有两件事在起作用:

  • 您只被允许订阅(即阅读)请求正文一次的事实
  • 从请求中获取表单数据需要读取和解析它

在 Spring Boot 2.0.x-2.1.x 中,HiddenHttpMethodFilter默认启用。此过滤器对于使用 HTML 表单中浏览器本身不支持的 HTTP 方法(如“DELETE”)很有用。如果传入请求属于该类型,则此过滤器需要解析表单数据。

可以说,这种用例越来越少见,并且从 Spring Boot 2.2 开始默认禁用

现在在您的示例中,读取和解析传入请求将不起作用,因为它已被过滤器解析。禁用过滤器将解决该问题。

但更好的解决方法是使用专用方法request.formData(),它将解析和缓存结果(如果多次调用,则返回已解析的数据)。

您可以在 Spring Framework 参考文档中阅读有关它的更多信息


推荐阅读