2

In Spring Boot, for Webflux projects, we request a stream of data by sending a header - "Accept: application/stream+json" in the HTTP Request.

If we send, "Accept: application/json", we get get a valid Json.

In Micronaut, however, if I send "Accept: application/stream+json", it throws an error.

{
    "message": "Specified Accept Types [application/x-json-stream] not supported. Supported types: [application/json]",
    "_links": {
        "self": {
            "href": "/carts/reactive",
            "templated": false
        }
    }
}

What is the equivalent of "Accept: application/stream+json" in Micronaut?

Anoop Hallimala
  • 625
  • 1
  • 11
  • 25
  • What is the signature of the `@Controller` method that is accepting the relevant request? – Jeff Scott Brown Feb 23 '21 at 20:59
  • It just has a @Get annotation on it. No produces, consumers mentioned. In spring boot, we can control the streaming with "Accept" header. Can't we do the same in Micronaut? – Anoop Hallimala Feb 24 '21 at 12:55
  • @JeffScottBrown @Get("/cart/reactive") public Mono getCartReactively() { return cartClient.getCartReactively() .doOnNext(cart -> log.info("Returned cart: " + cart)); } – Anoop Hallimala Feb 24 '21 at 13:22
  • did you try using "application/x-json-stream" as Accept header, i believe its the equivalent in Micronaut – Maninder Reddy Feb 24 '21 at 14:08
  • @ManinderReddy Yes. That was my original thought. It didn't work. I get the error "Specified Accept Types [application/x-json-stream] not supported" – Anoop Hallimala Feb 24 '21 at 14:09
  • Following code worked for me, and i passed 'Accept: application/x-json-stream' as header ` @Get(value = "/notification", produces = {MediaType.APPLICATION_JSON_STREAM}) public Flux getNotificationsById(@QueryValue("id") final String id) { return dataService.findById(id) .switchIfEmpty(Flux .error(new HttpStatusException(HttpStatus.NO_CONTENT, Flux.empty()))); } ` – Maninder Reddy Feb 25 '21 at 05:39
  • @ManinderReddy The question here is to how the client can control the response type - Json/stream. You are using produces = {MediaType.APPLICATION_JSON_STREAM, which means the return type is always stream. In spring boot, we can use Accept header to control what response type we want. I was expecting the same behaviour from Micronaut. – Anoop Hallimala Feb 25 '21 at 07:46

1 Answers1

2

What is the equivalent of "Accept: application/stream+json" in Micronaut?

As already mentioned in the comments, it's application/x-json-stream. (Sadly there's no one established standard for the content type for streaming json, at least not yet.)

The question here is to how the client can control the response type - Json/stream. You are using produces = {MediaType.APPLICATION_JSON_STREAM, which means the return type is always stream. In spring boot, we can use Accept header to control what response type we want. I was expecting the same behaviour from Micronaut.

Micronaut can do that too - you can pass more than one value to the produces parameter, and it will return either streamed or standard JSON accordingly:

@Get(value = "/", produces = {MediaType.APPLICATION_JSON_STREAM, MediaType.APPLICATION_JSON})
public Flux<Foo> getFoos() {
    return Flux.range(1, 3).map(i -> new Foo("Number " + i));
}

@Value
class Foo {
    private String content;
}

Then we can query the endpoint with either application/x-json-stream to retrieve a JSON stream:

 > curl localhost:8080 -H "Accept: application/x-json-stream"
{"content":"Number 1"}{"content":"Number 2"}{"content":"Number 3"}

...or plain application/json to retrieve a standard JSON array:

 > curl localhost:8080 -H "Accept: application/json"
[{"content":"Number 1"},{"content":"Number 2"},{"content":"Number 3"}]

If you want more custom control over the behaviour for each type of accept header, then you can just define separate controller methods entirely:

@Get(value = "/", produces = MediaType.APPLICATION_JSON_STREAM)
public Flux<Foo> getFoosStream() {
    return Flux.range(1, 3).map(i -> new Foo("Reactive " + i));
}

@Get(value = "/", produces = MediaType.APPLICATION_JSON)
public List<Foo> getFoosStandard() {
    return List.of(new Foo("Standard 1"), new Foo("Standard 2"), new Foo("Standard 3"));
}

...and depending on the header you send, a different method will be executed (as above you'll be able to see the difference with a standard curl command.)

Michael Berry
  • 70,193
  • 21
  • 157
  • 216