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

WebFlux returns 406 but Spring MVC renders "text/plain" request based on Publisher<?> and String [SPR-17078] #21615

Open
spring-issuemaster opened this issue Jul 24, 2018 · 5 comments

Comments

@spring-issuemaster
Copy link
Collaborator

@spring-issuemaster spring-issuemaster commented Jul 24, 2018

Dave Syer opened SPR-17078 and commented

I assume this is a Framework issue (might be Spring Boot I guess, since that's what I use to test it).

Controller:

@GetMapping("/straight")
public Mono<ResponseEntity<Publisher<?>>> straight() {
     return Mono.just(ResponseEntity.ok().header("x-foo", "spam")
               .body(Mono.from(Flux.just("foo", "bar"))));
}

Test:

@Test
public void straight() throws Exception {
     ResponseEntity<String> result = rest.exchange(RequestEntity
               .get(new URI("/straight")).accept(MediaType.TEXT_PLAIN).build(),
               String.class);
     assertThat(result.getBody()).isEqualTo("foo");
     assertThat(result.getHeaders()).containsKey("x-foo");
}

The test passes with MVC and fails with Webflux (406 not acceptable).


Affects: 5.0.7

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 24, 2018

Rossen Stoyanchev commented

In this case WebFlux depends on generic type information and Publisher<?> does not indicate what values should be expected. I confirmed Publisher<String> does work.

In Spring MVC it works because of the way it resolves async values first, and then dispatches to resume processing with the resolved values, at which point the value type is known. So it happens to work but in general with reactive types the expectation is that element type information needs to be available one way or another.

So this is expected behavior.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 25, 2018

Dave Syer commented

That's a bit disappointing. Isn't there a way to look at the concrete type at runtime? That way the same behaviour could be supported in Webflux and MVC. I can't change the signature of the controller because it doesn't always return a String.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 25, 2018

Rossen Stoyanchev commented

Not really because we need element type information in order to select the content type, to then choose an encoder, and only then can we produce values. In WebFlux the element type must be known to encode or decode.

I guess you could say Spring MVC has an advantage in its simpler view of all this where it decides upfront if the media type implies streaming, and if not it waits for the values to be resolved before making any decisions. The downside is that processing has the overhead of the extra async dispatches (1 request to kick off, a second async after unwrapping the Mono around ResponseEntity, and a third async after unwraping the Publisher body for final rendering), each time mapping to the controller, etc. That and the fact that all sync code must be kept separate.

What are the requirements for this controller? Perhaps it can be organized into multiple controller methods based on produces condition?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 26, 2018

Dave Syer commented

This sucks because if you don't specify a preference (no "accept" header) Spring happily renders the string and sends a 200. The problem then is that it is a JSON string, so it gets quoted, which is daft from a UX point of view. Is there really no way to detect the runtime type? Jackson can do it, apparently, if the JSON case works.

I'll try multiple controller methods as well. But that's kind of mad, and it's a workaround IMO. I potentially have to add a new method to the controller for every single media type.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Aug 16, 2018

Rossen Stoyanchev commented

On further thought, we probably could update AbstractMessageWriterResultHandler for a single value Publisher to first resolve the value, before deciding how to render.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
1 participant
You can’t perform that action at this time.