Micronaut HTTP Client - Parsing Response
I am using Micronaut 2.0 in this post. You can verify your version using the mn -Version
command.
Let us generate a controller using the create-controller
command provided by the Micronaut CLI as follows. I am launching the CLI from myapp
directory.
➜ myapp mn
mn> create-controller com.nareshak.demo.Greeting
| Rendered controller to src/main/java/com/nareshak/demo/GreetingController.java
| Rendered test to src/test/groovy/com/nareshak/demo/GreetingControllerSpec.groovy
Note how Micronaut suffixed Controller
to the value you supplied. Also, it generated a class to hold the corresponding tests. In this case, I am using the Spock framework for my tests (specified during the project creation). The generated test class looks as follows.
package com.nareshak.demo
import io.micronaut.http.client.annotation.Client
import io.micronaut.runtime.server.EmbeddedServer
import io.micronaut.test.annotation.MicronautTest
import io.micronaut.http.client.RxHttpClient
import io.micronaut.http.HttpResponse
import io.micronaut.http.HttpStatus
import spock.lang.AutoCleanup
import spock.lang.Specification
import spock.lang.Shared
import javax.inject.Inject
@MicronautTest
class GreetingControllerSpec extends Specification {
@Shared @Inject
EmbeddedServer embeddedServer
@Shared @AutoCleanup @Inject @Client("/")
RxHttpClient client
void "test index"() {
given:
HttpResponse response = client.toBlocking().exchange("/greeting")
expect:
response.status == HttpStatus.OK
}
}
At this point, if you try to run the test, it should pass.
Lets's go to the generated controller class GreetingController
and replace the return value from "Example Response" to "Hello". Your controller should now look as follows.
package com.nareshak.demo;
import io.micronaut.http.annotation.*;
@Controller("/greeting")
public class GreetingController {
@Get(uri="/", produces="text/plain")
public String index() {
return "Hello";
}
}
In our tests, we want to make sure that the GET call to endpoint "/greeting" indeed returns the value "Hello". So let's express that in our tests.
@MicronautTest
class GreetingControllerSpec extends Specification {
@Shared @Inject
EmbeddedServer embeddedServer
@Shared @AutoCleanup @Inject @Client("/")
RxHttpClient client
void "test index"() {
given:
HttpResponse response = client.toBlocking().exchange("/greeting")
expect:
response.status == HttpStatus.OK
and:
response.getContentType().get() == MediaType.TEXT_PLAIN_TYPE
and:
response.getBody(String).get() == 'Hello'
}
}
The response
object, which is of type io.micronaut.http.HttpResponse
has a method getBody
. It accepts the type(class) of response. Since we are expecting a String value, we pass String
as the argument. getBody
returns a java.util.Optional
type upon which I am calling get to fetch the value "Hello". However, when you run the test, you get the following failure message.
Condition failed with Exception:
response.getBody(String).get() == 'Hello'
| | | |
| | | java.util.NoSuchElementException: No value present
| | | at java.base/java.util.Optional.get(Optional.java:148)
| | | at com.nareshak.demo.GreetingControllerSpec.test index(GreetingControllerSpec.groovy:34)
| | class java.lang.String
| Optional.empty
<io.micronaut.http.client.netty.FullNettyClientHttpResponse@5db948c9 status=OK headers=io.micronaut.http.netty.NettyHttpHeaders@357c9bd9 attributes={} nettyHttpResponse=HttpObjectAggregator$AggregatedFullHttpResponse(decodeResult: success, version: HTTP/1.1, content: CompositeByteBuf(freed, components=1))
HTTP/1.1 200 OK
Surprising! Isn't' it? The response body doesn't contain any value.
Upon inspecting the API io.micronaut.http.client.BlockingHttpClient
, we find multiple overloaded methods with the name exchange
. The second argument we can pass to the method is bodyType
. If we don't specify one, the default value null
is assumed.
Since we didn't specify a bodyType
value, Micronaut HTTP client interpreted it as we are not interested in the response body and didn't put any efforts to parse the response. After all, being lazy is a trait of intelligence in the world of reactive-programming!
Let's supply that second argument to exchange
method and re-run our tests.
void "test index"() {
given:
HttpResponse response = client.toBlocking().exchange("/greeting", String)
expect:
response.status == HttpStatus.OK
and:
response.getContentType().get() == MediaType.TEXT_PLAIN_TYPE
and:
response.getBody(String).get() == 'Hello'
}
Now our tests are passing. If you have faced this problem already, you may be screaming - "Eureka, body found!"
In most common cases your return type would be a Java class representing the structure of your response.
If you just want to get the response body and thinking it's a lot of work, checkout this post.
Micronaut version used: 2.0.0