URI Path Variables
URI variables can be referenced via method arguments. For example:
URI Variables Example
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.PathVariable;
@Controller("/issues") (1)
public class IssuesController {
@Get("/{number}") (2)
public String issue(@PathVariable Integer number) { (3)
return "Issue # " + number + "!"; (4)
}
}
URI Variables Example
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.PathVariable
@Controller("/issues") (1)
class IssuesController {
@Get("/{number}") (2)
String issue(@PathVariable Integer number) { (3)
"Issue # " + number + "!" (4)
}
}
URI Variables Example
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.PathVariable
@Controller("/issues") (1)
class IssuesController {
@Get("/{number}") (2)
fun issue(@PathVariable number: Int): String { (3)
return "Issue # $number!" (4)
}
}
1 | The @Controller annotation is specified with a base URI of /issues |
2 | The Get annotation is used to map the method to an HTTP GET with a URI variable embedded in the URI called number |
3 | The method argument can be optionally annotated with PathVariable |
4 | The value of the URI variable is referenced in the implementation |
Micronaut will map the URI /issues/{number}
for the above controller. We can assert this is the case by writing a set of unit tests:
Testing URI Variables
import io.micronaut.context.ApplicationContext;
import io.micronaut.http.client.HttpClient;
import io.micronaut.http.client.exceptions.HttpClientResponseException;
import io.micronaut.runtime.server.EmbeddedServer;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class IssuesControllerTest {
private static EmbeddedServer server;
private static HttpClient client;
@BeforeClass (1)
public static void setupServer() {
server = ApplicationContext.run(EmbeddedServer.class);
client = server
.getApplicationContext()
.createBean(HttpClient.class, server.getURL());
}
@AfterClass (2)
public static void stopServer() {
if(server != null) {
server.stop();
}
if(client != null) {
client.stop();
}
}
@Test
public void testIssue() throws Exception {
String body = client.toBlocking().retrieve("/issues/12"); (3)
assertNotNull(body);
assertEquals("Issue # 12!", body); (4)
}
@Test
public void testShowWithInvalidInteger() {
HttpClientResponseException e =Assertions.assertThrows(HttpClientResponseException.class, () ->
client.toBlocking().exchange("/issues/hello"));
assertEquals(400, e.getStatus().getCode()); (5)
}
@Test
public void testIssueWithoutNumber() {
HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () ->
client.toBlocking().exchange("/issues/"));
assertEquals(404, e.getStatus().getCode()); (6)
}
}
Testing URI Variables
import io.micronaut.context.ApplicationContext
import io.micronaut.http.client.HttpClient
import io.micronaut.http.client.exceptions.HttpClientResponseException
import io.micronaut.runtime.server.EmbeddedServer
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification
class IssuesControllerTest extends Specification {
@Shared
@AutoCleanup (2)
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer) (1)
@Shared
@AutoCleanup (2)
HttpClient client = HttpClient.create(embeddedServer.URL) (1)
void "test issue"() {
when:
String body = client.toBlocking().retrieve("/issues/12") (3)
then:
body != null
body == "Issue # 12!" (4)
}
void "/issues/{number} with an invalid Integer number responds 400"() {
when:
client.toBlocking().exchange("/issues/hello")
then:
HttpClientResponseException e = thrown(HttpClientResponseException)
e.status.code == 400 (5)
}
void "/issues/{number} without number responds 404"() {
when:
client.toBlocking().exchange("/issues/")
then:
HttpClientResponseException e = thrown(HttpClientResponseException)
e.status.code == 404 (6)
}
}
Testing URI Variables
import io.micronaut.context.ApplicationContext
import io.micronaut.http.client.exceptions.HttpClientResponseException
import io.micronaut.runtime.server.EmbeddedServer
import io.kotlintest.shouldBe
import io.kotlintest.shouldNotBe
import io.kotlintest.shouldThrow
import io.kotlintest.specs.StringSpec
import io.micronaut.http.client.RxHttpClient
class IssuesControllerTest: StringSpec() {
val embeddedServer = autoClose( (2)
ApplicationContext.run(EmbeddedServer::class.java) (1)
)
val client = autoClose( (2)
embeddedServer.applicationContext.createBean(RxHttpClient::class.java, embeddedServer.getURL()) (1)
)
init {
"test issue" {
val body = client.toBlocking().retrieve("/issues/12") (3)
body shouldNotBe null
body shouldBe "Issue # 12!" (4)
}
"test issue with invalid integer" {
val e = shouldThrow<HttpClientResponseException> { client.toBlocking().exchange<Any>("/issues/hello") }
e.status.code shouldBe 400 (5)
}
"test issue without number" {
val e = shouldThrow<HttpClientResponseException> { client.toBlocking().exchange<Any>("/issues/") }
e.status.code shouldBe 404 (6)
}
}
}
1 | The embedded server and http client is being started |
2 | The server and client will be cleaned up after all of the tests have finished |
3 | The tests sends a request to the URI /issues/12 |
4 | And then asserts the response is “Issue # 12” |
5 | Another test asserts a 400 response is returned when an invalid number is sent in the URL |
6 | Another test asserts a 404 response is returned when no number is provided in the URL. The variable being present is required in order for the route to be executed. |
Note that the URI template in the previous example requires that the number
variable is specified. You can specify optional URI templates with the syntax: /issues{/number}
and by annotating the number
parameter with @Nullable
.
The following table provides some examples of URI templates and what they match:
Template | Description | Matching URI |
---|---|---|
| Simple match |
|
| A variable of 2 characters max |
|
| An optional URI variable |
|
| An optional URI variable with regex |
|
| Optional query parameters |
|
| Regex path match with extension |
|