Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multipart form data can no longer be sent with syncBody in WebTestClient [SPR-16131] #20679

Closed
spring-projects-issues opened this issue Oct 30, 2017 · 3 comments
Assignees
Labels
in: test Issues in the test module type: bug A general bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Oct 30, 2017

Andy Wilkinson opened SPR-16131 and commented

I just upgraded to Framework 5.0.1 and noticed a regression when using syncBody to send multipart form data. With 5.0.0, this worked:

MultiValueMap<String, Object> multipartData = new LinkedMultiValueMap<>();
multipartData.add("file", new ByteArrayResource(new byte[] { 1, 2, 3, 4 }) {

	@Override
	public String getFilename() {
		return "image.png";
	}

});
ExchangeResult result = WebTestClient
		.bindToRouterFunction(RouterFunctions.route(POST("/foo"), (req) -> {
			req.body(BodyExtractors.toMultipartData()).block();
			return ServerResponse.ok().build();
		})).configureClient().baseUrl("http://localhost").build().post()
		.uri("/foo").syncBody(multipartData).exchange().expectBody()
		.returnResult();

Following the upgrade to 5.0.1 it appears to be trying to send form data rather than multipart data and, as a result, it fails:

java.lang.ClassCastException: org.springframework.restdocs.webtestclient.WebTestClientRequestConverterTests$1 cannot be cast to java.lang.String
	at org.springframework.http.codec.FormHttpMessageWriter.generateForm(FormHttpMessageWriter.java:127)
	at org.springframework.http.codec.FormHttpMessageWriter.lambda$write$0(FormHttpMessageWriter.java:102)
	at org.springframework.http.codec.FormHttpMessageWriter$$Lambda$185/198499365.apply(Unknown Source)
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:107)
	at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:1649)
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:156)
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:103)
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90)
	at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54)
	at reactor.core.publisher.MonoMapFuseable.subscribe(MonoMapFuseable.java:59)
	at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)
	at reactor.core.publisher.Mono.subscribe(Mono.java:2913)
	at reactor.core.publisher.Mono.subscribeWith(Mono.java:3021)
	at reactor.core.publisher.Mono.subscribe(Mono.java:2907)
	at reactor.core.publisher.Mono.subscribe(Mono.java:2874)
	at reactor.core.publisher.Mono.subscribe(Mono.java:2846)
	at org.springframework.test.web.reactive.server.HttpHandlerConnector.connect(HttpHandlerConnector.java:101)
	at org.springframework.test.web.reactive.server.WiretapConnector.connect(WiretapConnector.java:61)
	at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.exchange(ExchangeFunctions.java:74)
	at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.exchange(DefaultWebClient.java:327)
	at org.springframework.test.web.reactive.server.DefaultWebTestClient$DefaultRequestBodyUriSpec.exchange(DefaultWebTestClient.java:282)
	at org.springframework.restdocs.webtestclient.WebTestClientRequestConverterTests.multipartUploadFromResource(WebTestClientRequestConverterTests.java:191)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
	Suppressed: java.lang.Exception: #block terminated with an error
		at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:126)
		at reactor.core.publisher.Mono.block(Mono.java:1185)
		... 25 more

It's trying to cast the anonymous ByteArrayResource subclass to a String. I can work around the problem by using .body(BodyInserters.fromMultipartData(multipartData)) in place of .syncBody(multipartData).


Affects: 5.0.1

Issue Links:

Referenced from: commits 8083eaa

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

This seems to be related to the following change 9df6f3 which relaxed the constraint for FormHttpMessageWriter to work only with MultiValueMap declared with <String, String> generic types. So while it looks like a regression we actually have a design issue since both the Form~ and Multipart~ writer support MultiValueMap. By comparison FromHttpMessageConverter is a single class that supports writing both and in the write method checks the content of the map, so it doesn't depend on generics.

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

Actually this is the commit 2962f0.

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

This should be fixed now in master. See commit 8083ea for details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants