2.4 Creating a Client

As mentioned previously, Micronaut includes both an HTTP server and an HTTP client. A low-level HTTP client is provided which you can use to test the HelloController created in the previous section.

  1. import io.micronaut.http.HttpRequest;
  2. import io.micronaut.http.client.HttpClient;
  3. import io.micronaut.http.client.annotation.Client;
  4. import io.micronaut.runtime.server.EmbeddedServer;
  5. import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
  6. import org.junit.jupiter.api.Test;
  7. import jakarta.inject.Inject;
  8. import static org.junit.jupiter.api.Assertions.assertEquals;
  9. @MicronautTest
  10. public class HelloControllerSpec {
  11. @Inject
  12. EmbeddedServer server; (1)
  13. @Inject
  14. @Client("/")
  15. HttpClient client; (2)
  16. @Test
  17. void testHelloWorldResponse() {
  18. String response = client.toBlocking() (3)
  19. .retrieve(HttpRequest.GET("/hello"));
  20. assertEquals("Hello World", response); (4)
  21. }
  22. }
  1. import io.micronaut.http.HttpRequest
  2. import io.micronaut.http.client.HttpClient
  3. import io.micronaut.http.client.annotation.Client
  4. import io.micronaut.runtime.server.EmbeddedServer
  5. import io.micronaut.test.extensions.spock.annotation.MicronautTest
  6. import spock.lang.Specification
  7. import jakarta.inject.Inject
  8. @MicronautTest
  9. class HelloControllerSpec extends Specification {
  10. @Inject
  11. EmbeddedServer embeddedServer (1)
  12. @Inject
  13. @Client("/")
  14. HttpClient client (2)
  15. void "test hello world response"() {
  16. expect:
  17. client.toBlocking() (3)
  18. .retrieve(HttpRequest.GET('/hello')) == "Hello World" (4)
  19. }
  20. }
  1. import io.micronaut.http.client.HttpClient
  2. import io.micronaut.http.client.annotation.Client
  3. import io.micronaut.runtime.server.EmbeddedServer
  4. import io.micronaut.test.extensions.junit5.annotation.MicronautTest
  5. import org.junit.jupiter.api.Assertions.assertEquals
  6. import org.junit.jupiter.api.Test
  7. import jakarta.inject.Inject
  8. @MicronautTest
  9. class HelloControllerSpec {
  10. @Inject
  11. lateinit var server: EmbeddedServer (1)
  12. @Inject
  13. @field:Client("/")
  14. lateinit var client: HttpClient (2)
  15. @Test
  16. fun testHelloWorldResponse() {
  17. val rsp: String = client.toBlocking() (3)
  18. .retrieve("/hello")
  19. assertEquals("Hello World", rsp) (4)
  20. }
  21. }
1The EmbeddedServer is configured as a shared test field
2A HttpClient instance shared field is also defined
3The test uses the toBlocking() method to make a blocking call
4The retrieve method returns the controller response as a String

In addition to a low-level client, Micronaut features a declarative, compile-time HTTP client, powered by the Client annotation.

To create a client, create an interface annotated with @Client, for example:

  1. import io.micronaut.http.MediaType;
  2. import io.micronaut.http.annotation.Get;
  3. import io.micronaut.http.client.annotation.Client;
  4. import org.reactivestreams.Publisher;
  5. import io.micronaut.core.async.annotation.SingleResult;
  6. @Client("/hello") (1)
  7. public interface HelloClient {
  8. @Get(consumes = MediaType.TEXT_PLAIN) (2)
  9. @SingleResult
  10. Publisher<String> hello(); (3)
  11. }
  1. import io.micronaut.http.annotation.Get
  2. import io.micronaut.http.client.annotation.Client
  3. import org.reactivestreams.Publisher
  4. import io.micronaut.core.async.annotation.SingleResult
  5. @Client("/hello") (1)
  6. interface HelloClient {
  7. @Get(consumes = MediaType.TEXT_PLAIN) (2)
  8. @SingleResult
  9. Publisher<String> hello() (3)
  10. }
  1. import io.micronaut.http.MediaType
  2. import io.micronaut.http.annotation.Get
  3. import io.micronaut.http.client.annotation.Client
  4. import io.micronaut.core.async.annotation.SingleResult
  5. import org.reactivestreams.Publisher
  6. @Client("/hello") (1)
  7. interface HelloClient {
  8. @Get(consumes = [MediaType.TEXT_PLAIN]) (2)
  9. @SingleResult
  10. fun hello(): Publisher<String> (3)
  11. }
1The @Client annotation is used with a value that is a relative path to the current server
2The same @Get annotation used on the server is used to define the client mapping
3A Publisher annotated with SingleResult is returned with the value read from the server

To test the HelloClient, retrieve it from the ApplicationContext associated with the server:

  1. import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
  2. import org.junit.jupiter.api.Test;
  3. import io.micronaut.core.async.annotation.SingleResult;
  4. import jakarta.inject.Inject;
  5. import reactor.core.publisher.Mono;
  6. import static org.junit.jupiter.api.Assertions.assertEquals;
  7. @MicronautTest (1)
  8. public class HelloClientSpec {
  9. @Inject
  10. HelloClient client; (2)
  11. @Test
  12. public void testHelloWorldResponse(){
  13. assertEquals("Hello World", Mono.from(client.hello()).block());(3)
  14. }
  15. }
  1. import io.micronaut.test.extensions.spock.annotation.MicronautTest
  2. import reactor.core.publisher.Mono
  3. import spock.lang.Specification
  4. import jakarta.inject.Inject
  5. @MicronautTest (1)
  6. class HelloClientSpec extends Specification {
  7. @Inject HelloClient client (2)
  8. void "test hello world response"() {
  9. expect:
  10. Mono.from(client.hello()).block() == "Hello World" (3)
  11. }
  12. }
  1. import io.micronaut.context.annotation.Property
  2. import io.micronaut.test.extensions.junit5.annotation.MicronautTest
  3. import org.junit.jupiter.api.Assertions.assertEquals
  4. import org.junit.jupiter.api.Test
  5. import jakarta.inject.Inject
  6. import reactor.core.publisher.Mono
  7. @MicronautTest (1)
  8. class HelloClientSpec {
  9. @Inject
  10. lateinit var client: HelloClient (2)
  11. @Test
  12. fun testHelloWorldResponse() {
  13. assertEquals("Hello World", Mono.from(client.hello()).block())(3)
  14. }
  15. }
1The @MicronautTest annotation defines the test
2The HelloClient is injected from the ApplicationContext
3The client is invoked using the Project Reactor Mono::block method

The Client annotation produces an implementation automatically for you at compile time without the using proxies or runtime reflection.

The Client annotation is very flexible. See the section on the Micronaut HTTP Client for more information.