6.9 The HttpRequest and HttpResponse

If you need more control over request processing you can write a method that receives the complete HttpRequest.

In fact, there are several higher-level interfaces that can be bound to controller method parameters. These include:

Table 1. Bindable Micronaut Interfaces
InterfaceDescriptionExample

HttpRequest

The full HttpRequest

String hello(HttpRequest request)

HttpHeaders

All HTTP headers present in the request

String hello(HttpHeaders headers)

HttpParameters

All HTTP parameters (either from URI variables or request parameters) present in the request

String hello(HttpParameters params)

Cookies

All Cookies present in the request

String hello(Cookies cookies)

The HttpRequest should be declared parametrized with a concrete generic type if the request body is needed, e.g. HttpRequest<MyClass> request. The body may not be available from the request otherwise.

In addition, for full control over the emitted HTTP response you can use the static factory methods of the HttpResponse class which return a MutableHttpResponse.

The following example implements the previous MessageController example using the HttpRequest and HttpResponse objects:

Request and Response Example

  1. import io.micronaut.http.HttpRequest;
  2. import io.micronaut.http.HttpResponse;
  3. import io.micronaut.http.annotation.Controller;
  4. import io.micronaut.http.annotation.Get;
  5. import io.micronaut.http.context.ServerRequestContext;
  6. import reactor.core.publisher.Mono;
  7. @Controller("/request")
  8. public class MessageController {
  9. @Get("/hello") (1)
  10. public HttpResponse<String> hello(HttpRequest<?> request) {
  11. String name = request.getParameters()
  12. .getFirst("name")
  13. .orElse("Nobody"); (2)
  14. return HttpResponse.ok("Hello " + name + "!!")
  15. .header("X-My-Header", "Foo"); (3)
  16. }
  17. }

Request and Response Example

  1. import io.micronaut.http.HttpRequest
  2. import io.micronaut.http.HttpResponse
  3. import io.micronaut.http.annotation.Controller
  4. import io.micronaut.http.annotation.Get
  5. import io.micronaut.http.context.ServerRequestContext
  6. import reactor.core.publisher.Mono
  7. @Controller("/request")
  8. class MessageController {
  9. @Get("/hello") (1)
  10. HttpResponse<String> hello(HttpRequest<?> request) {
  11. String name = request.parameters
  12. .getFirst("name")
  13. .orElse("Nobody") (2)
  14. HttpResponse.ok("Hello " + name + "!!")
  15. .header("X-My-Header", "Foo") (3)
  16. }
  17. }

Request and Response Example

  1. import io.micronaut.http.HttpRequest
  2. import io.micronaut.http.HttpResponse
  3. import io.micronaut.http.annotation.Controller
  4. import io.micronaut.http.annotation.Get
  5. import io.micronaut.http.context.ServerRequestContext
  6. import reactor.core.publisher.Mono
  7. import reactor.util.context.ContextView
  8. @Controller("/request")
  9. class MessageController {
  10. @Get("/hello") (1)
  11. fun hello(request: HttpRequest<*>): HttpResponse<String> {
  12. val name = request.parameters
  13. .getFirst("name")
  14. .orElse("Nobody") (2)
  15. return HttpResponse.ok("Hello $name!!")
  16. .header("X-My-Header", "Foo") (3)
  17. }
  18. }
1The method is mapped to the URI /hello and accepts a HttpRequest
2The HttpRequest is used to obtain the value of a query parameter named name.
3The HttpResponse.ok(T) method returns a MutableHttpResponse with a text body. A header named X-My-Header is also added to the response.

The HttpRequest is also available from a static context via ServerRequestContext.

Using the ServerRequestContext

  1. import io.micronaut.http.HttpRequest;
  2. import io.micronaut.http.HttpResponse;
  3. import io.micronaut.http.annotation.Controller;
  4. import io.micronaut.http.annotation.Get;
  5. import io.micronaut.http.context.ServerRequestContext;
  6. import reactor.core.publisher.Mono;
  7. @Controller("/request")
  8. public class MessageController {
  9. @Get("/hello-static") (1)
  10. public HttpResponse<String> helloStatic() {
  11. HttpRequest<?> request = ServerRequestContext.currentRequest() (1)
  12. .orElseThrow(() -> new RuntimeException("No request present"));
  13. String name = request.getParameters()
  14. .getFirst("name")
  15. .orElse("Nobody");
  16. return HttpResponse.ok("Hello " + name + "!!")
  17. .header("X-My-Header", "Foo");
  18. }
  19. }

Using the ServerRequestContext

  1. import io.micronaut.http.HttpRequest
  2. import io.micronaut.http.HttpResponse
  3. import io.micronaut.http.annotation.Controller
  4. import io.micronaut.http.annotation.Get
  5. import io.micronaut.http.context.ServerRequestContext
  6. import reactor.core.publisher.Mono
  7. @Controller("/request")
  8. class MessageController {
  9. @Get("/hello-static") (1)
  10. HttpResponse<String> helloStatic() {
  11. HttpRequest<?> request = ServerRequestContext.currentRequest() (1)
  12. .orElseThrow(() -> new RuntimeException("No request present"))
  13. String name = request.parameters
  14. .getFirst("name")
  15. .orElse("Nobody")
  16. HttpResponse.ok("Hello " + name + "!!")
  17. .header("X-My-Header", "Foo")
  18. }
  19. }

Using the ServerRequestContext

  1. import io.micronaut.http.HttpRequest
  2. import io.micronaut.http.HttpResponse
  3. import io.micronaut.http.annotation.Controller
  4. import io.micronaut.http.annotation.Get
  5. import io.micronaut.http.context.ServerRequestContext
  6. import reactor.core.publisher.Mono
  7. import reactor.util.context.ContextView
  8. @Controller("/request")
  9. class MessageController {
  10. @Get("/hello-static") (1)
  11. fun helloStatic(): HttpResponse<String> {
  12. val request: HttpRequest<*> = ServerRequestContext.currentRequest<Any>() (1)
  13. .orElseThrow { RuntimeException("No request present") }
  14. val name = request.parameters
  15. .getFirst("name")
  16. .orElse("Nobody")
  17. return HttpResponse.ok("Hello $name!!")
  18. .header("X-My-Header", "Foo")
  19. }
  20. }
1The ServerRequestContext is used to retrieve the request.
Generally ServerRequestContext is available within reactive flow, but the recommended approach is consume the request as an argument as shown in the previous example. If the request is needed in downstream methods it should be passed as an argument to those methods. There are cases where the context is not propagated because other threads are used to emit the data.

An alternative for users of Project Reactor to using the ServerRequestContext is to use the contextual features of Project Reactor to retrieve the request. Because the Micronaut Framework uses Project Reactor as it’s default reactive streams implementation, users of Project Reactor can benefit by being able to access the request in the context. For example:

Using the Project Reactor context

  1. import io.micronaut.http.HttpRequest;
  2. import io.micronaut.http.HttpResponse;
  3. import io.micronaut.http.annotation.Controller;
  4. import io.micronaut.http.annotation.Get;
  5. import io.micronaut.http.context.ServerRequestContext;
  6. import reactor.core.publisher.Mono;
  7. @Controller("/request")
  8. public class MessageController {
  9. @Get("/hello-reactor")
  10. public Mono<HttpResponse<String>> helloReactor() {
  11. return Mono.deferContextual(ctx -> { (1)
  12. HttpRequest<?> request = ctx.get(ServerRequestContext.KEY); (2)
  13. String name = request.getParameters()
  14. .getFirst("name")
  15. .orElse("Nobody");
  16. return Mono.just(HttpResponse.ok("Hello " + name + "!!")
  17. .header("X-My-Header", "Foo"));
  18. });
  19. }
  20. }

Using the Project Reactor context

  1. import io.micronaut.http.HttpRequest
  2. import io.micronaut.http.HttpResponse
  3. import io.micronaut.http.annotation.Controller
  4. import io.micronaut.http.annotation.Get
  5. import io.micronaut.http.context.ServerRequestContext
  6. import reactor.core.publisher.Mono
  7. @Controller("/request")
  8. class MessageController {
  9. @Get("/hello-reactor")
  10. Mono<HttpResponse<String>> helloReactor() {
  11. Mono.deferContextual(ctx -> { (1)
  12. HttpRequest<?> request = ctx.get(ServerRequestContext.KEY) (2)
  13. String name = request.parameters
  14. .getFirst("name")
  15. .orElse("Nobody")
  16. Mono.just(HttpResponse.ok("Hello " + name + "!!")
  17. .header("X-My-Header", "Foo"))
  18. })
  19. }
  20. }

Using the Project Reactor context

  1. import io.micronaut.http.HttpRequest
  2. import io.micronaut.http.HttpResponse
  3. import io.micronaut.http.annotation.Controller
  4. import io.micronaut.http.annotation.Get
  5. import io.micronaut.http.context.ServerRequestContext
  6. import reactor.core.publisher.Mono
  7. import reactor.util.context.ContextView
  8. @Controller("/request")
  9. class MessageController {
  10. @Get("/hello-reactor")
  11. fun helloReactor(): Mono<HttpResponse<String>?>? {
  12. return Mono.deferContextual { ctx: ContextView -> (1)
  13. val request = ctx.get<HttpRequest<*>>(ServerRequestContext.KEY) (2)
  14. val name = request.parameters
  15. .getFirst("name")
  16. .orElse("Nobody")
  17. Mono.just(HttpResponse.ok("Hello $name!!")
  18. .header("X-My-Header", "Foo"))
  19. }
  20. }
  21. }
1The Mono is created with a reference to the context
2The request is retrieved from the context

Using the context to retrieve the request is the best approach for reactive flows because Project Reactor propagates the context and it does not rely on a thread local like ServerRequestContext.