Spring-启动 WebFlux getFormData 无法解析真正的 x-www-form-urlencoded 数据?
Spring-Boot WebFlux getFormData fails to parse real x-www-form-urlencoded data?
我用最新最好的 Spring-Boot V2.2.5 和 WebFlux 启动器做了一个小测试项目。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- ... -->
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- ... -->
在那个项目中,我创建了一个非常简单的 @RestController 解析 x-www-form-urlencoded,如下所示:
@RestController
public class MyController {
/*
This worked in Tomcat stack, but it's unfortunately too old-school for WebFlux...
java.lang.IllegalStateException: In a WebFlux application, form data is accessed via ServerWebExchange.getFormData().
at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.readBody(AbstractMessageReaderArgumentResolver.java:158) ~[spring-webflux-5.2.4.RELEASE.jar:5.2.4.RELEASE]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ HTTP POST "/" [ExceptionHandlingWebHandler]
Stack trace:
...
@PostMapping
public Map<String, String> test(@RequestBody MultiValueMap<String, String> formData) {
String formDataTest = formData.getFirst("test");
String result = Objects.requireNonNullElse(formDataTest, "you failed!");
return Map.of("result", result);
}
*/
@PostMapping
public Map<String, String> test(ServerWebExchange serverWebExchange) {
MultiValueMap<String, String> formData = getFormData(serverWebExchange);
String formDataTest = formData.getFirst("test");
String result = Objects.requireNonNullElse(formDataTest, "you failed!");
return Map.of("result", result);
}
private static MultiValueMap<String, String> getFormData(ServerWebExchange serverWebExchange) {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
serverWebExchange.getFormData().subscribe(formData::addAll);
return formData;
}
}
为了测试这一点,我写了以下内容...
@WebFluxTest(controllers = MyController.class)
class MyControllerTest {
@Autowired
private WebTestClient webClient;
@Test
void test() {
webClient.post()
.uri("/")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData("test", "testing-test"))
.exchange()
.expectStatus().is2xxSuccessful()
.expectBody()
.jsonPath("$.result").isEqualTo("testing-test");
}
}
...测试变为绿色。很棒的东西!
不幸的是,这对于真实世界的数据似乎失败了。在 Postman 和 curl 中进行了测试 - 根据我的理解,此查询应该可以正常工作,但是...
➜ ~ curl --location --request POST 'localhost:8080' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'test=testing-test'
{"result":"you failed!"}%
我是不是做错了什么?为什么用curl查询没有from-data解析出来?
在 webflux 中,你有一个订阅者(消费者)和一个发布者。
一个发布,一个消费。您的应用程序向消费者发布数据。因此,您有一个想要使用数据的客户端 (curl)。要使用它,curl 会订阅一个 webflux 流,而 webflux 会传送一个或多个对象。如果它是一个对象,它会传递一个 Mono<T>
如果它是多个对象,它会传递一个 Flux<T>
.
我为什么要解释这个?
好吧:
serverWebExchange.getFormData().subscribe(formData::addAll)
您正在应用程序内部订阅。一旦你订阅,你就离开了反应世界,你失去了拥有 webflux 应用程序的所有好处,你消耗了数据。
所以最重要的是,您(几乎)不应该在自己的应用程序中订阅。如果您的应用程序是发起调用和消费的应用程序,那么请确保它可以订阅。但是这里是curl订阅了想要消费,所以你不应该。
您应该 return 来自 @PostMapping
注释函数的 Mono<Map<String, String>>
。
public Mono<Map<String, String>> test(ServerWebExchange serverWebExchange)
然后重写为return一个Mono:
private static Mono<MultiValueMap<String, String> >getFormData(ServerWebExchange serverWebExchange) {
return serverWebExchange.getFormData()
.flatMap(formData -> {
MultiValueMap<String, String> formDataResponse = new LinkedMultiValueMap<>();
return Mono.just(formDataResponse.addAll(formData));
});
}
我用最新最好的 Spring-Boot V2.2.5 和 WebFlux 启动器做了一个小测试项目。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- ... -->
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- ... -->
在那个项目中,我创建了一个非常简单的 @RestController 解析 x-www-form-urlencoded,如下所示:
@RestController
public class MyController {
/*
This worked in Tomcat stack, but it's unfortunately too old-school for WebFlux...
java.lang.IllegalStateException: In a WebFlux application, form data is accessed via ServerWebExchange.getFormData().
at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.readBody(AbstractMessageReaderArgumentResolver.java:158) ~[spring-webflux-5.2.4.RELEASE.jar:5.2.4.RELEASE]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ HTTP POST "/" [ExceptionHandlingWebHandler]
Stack trace:
...
@PostMapping
public Map<String, String> test(@RequestBody MultiValueMap<String, String> formData) {
String formDataTest = formData.getFirst("test");
String result = Objects.requireNonNullElse(formDataTest, "you failed!");
return Map.of("result", result);
}
*/
@PostMapping
public Map<String, String> test(ServerWebExchange serverWebExchange) {
MultiValueMap<String, String> formData = getFormData(serverWebExchange);
String formDataTest = formData.getFirst("test");
String result = Objects.requireNonNullElse(formDataTest, "you failed!");
return Map.of("result", result);
}
private static MultiValueMap<String, String> getFormData(ServerWebExchange serverWebExchange) {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
serverWebExchange.getFormData().subscribe(formData::addAll);
return formData;
}
}
为了测试这一点,我写了以下内容...
@WebFluxTest(controllers = MyController.class)
class MyControllerTest {
@Autowired
private WebTestClient webClient;
@Test
void test() {
webClient.post()
.uri("/")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData("test", "testing-test"))
.exchange()
.expectStatus().is2xxSuccessful()
.expectBody()
.jsonPath("$.result").isEqualTo("testing-test");
}
}
...测试变为绿色。很棒的东西!
不幸的是,这对于真实世界的数据似乎失败了。在 Postman 和 curl 中进行了测试 - 根据我的理解,此查询应该可以正常工作,但是...
➜ ~ curl --location --request POST 'localhost:8080' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'test=testing-test'
{"result":"you failed!"}%
我是不是做错了什么?为什么用curl查询没有from-data解析出来?
在 webflux 中,你有一个订阅者(消费者)和一个发布者。
一个发布,一个消费。您的应用程序向消费者发布数据。因此,您有一个想要使用数据的客户端 (curl)。要使用它,curl 会订阅一个 webflux 流,而 webflux 会传送一个或多个对象。如果它是一个对象,它会传递一个 Mono<T>
如果它是多个对象,它会传递一个 Flux<T>
.
我为什么要解释这个?
好吧:
serverWebExchange.getFormData().subscribe(formData::addAll)
您正在应用程序内部订阅。一旦你订阅,你就离开了反应世界,你失去了拥有 webflux 应用程序的所有好处,你消耗了数据。
所以最重要的是,您(几乎)不应该在自己的应用程序中订阅。如果您的应用程序是发起调用和消费的应用程序,那么请确保它可以订阅。但是这里是curl订阅了想要消费,所以你不应该。
您应该 return 来自 @PostMapping
注释函数的 Mono<Map<String, String>>
。
public Mono<Map<String, String>> test(ServerWebExchange serverWebExchange)
然后重写为return一个Mono:
private static Mono<MultiValueMap<String, String> >getFormData(ServerWebExchange serverWebExchange) {
return serverWebExchange.getFormData()
.flatMap(formData -> {
MultiValueMap<String, String> formDataResponse = new LinkedMultiValueMap<>();
return Mono.just(formDataResponse.addAll(formData));
});
}