Micronaut doesn't return default HTTP error representation for Flux return types

Viewed 545

I have simple Micronaut controller with 2 similar GET methods which return different reactive types.

@Controller
public class MyController {
    
    @Get(uri = "/a", produces = MediaType.APPLICATION_JSON_STREAM)
    public Flux<MyPojo> getA() {
        return Flux.just("Hi")
                .flatMap(s -> Mono.error(new HttpStatusException(HttpStatus.BAD_REQUEST, s)))
                .map(o -> new MyPojo());
    }

    @Get(uri = "/b", produces = MediaType.APPLICATION_JSON_STREAM)
    public Mono<MyPojo> getB() {
        return Mono.just("Hi")
                .flatMap(s -> Mono.error(new HttpStatusException(HttpStatus.BAD_REQUEST, s)))
                .map(o -> new MyPojo());
    }
                
}

Now, if I invoke endpoint /b I'll get HTTP 400 with default error representation io.micronaut.http.hateoas.JsonError provided by io.micronaut.http.server.exceptions.HttpStatusHandler which is desired and perfect.

{
  "message": "Bad Request",
  "_embedded": {
    "errors": [
      {
        "message": "Hi"
      }
    ]
  },
  "_links": {
    "self": {
      "href": "/b",
      "templated": false
    }
  }
}

However, if I'll invoke endpoint /a I'll get HTTP 500 error without any body. Log file reports following error:

2022-01-12 00:45:31.385 ERROR [default-nioEventLoopGroup-2-2] i.m.h.netty.stream.HttpStreamsHandler    : Error occurred writing stream response: Hi
io.micronaut.http.exceptions.HttpStatusException: Hi
    at com.example.MyController.lambda$getA$6(MyController.java:199)
    at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:386)
    at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onNext(ReactorSubscriber.java:57)
    at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2398)
    at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:371)
    at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onSubscribe(ReactorSubscriber.java:50)
    at reactor.core.publisher.FluxJust.subscribe(FluxJust.java:68)
    at reactor.core.publisher.Flux.subscribe(Flux.java:8468)
    ...

which seems correct...

The question is - how to get correct HTTP code (400) and default error representation (JsonError) in case of endpoint /a?

1 Answers

the /a endpoint indicates streaming because you're returning a non single publisher type. Streamed responses are handled differently and we switch to the mode of writing the response when the error is encountered. At this point it isn't feasible to go back to create another response because an OK response has already been sent down the Netty pipeline.

See https://github.com/micronaut-projects/micronaut-core/issues/5581

Related