Sunday, 18 June 2023

Micronaut: URI path variables

Micronaut @PathVariable annotation bind segments of paths to method parameters.

 

For example, the api to get the employee details by id looks like below.

/employees/by-id/{employeeId}

@Get("/by-id/{employeeId}")
public Employee byId(@NonNull @PathVariable(name = "employeeId") final Integer empId) {
    return employeeService.byId(empId);
}

 

For the url '/employees/by-id/1', the argument empId is bound to the value 1.

 

Find the below working application.

 

Step 1: Create new maven project ‘micronaut-rest-path-variable’.

 

Step 2: Update pom.xml with maven dependencies.

 

pom.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sample.app</groupId>
    <artifactId>micronaut-rest-path-variable</artifactId>
    <version>0.1</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>io.micronaut</groupId>
        <artifactId>micronaut-parent</artifactId>
        <version>3.7.4</version>
    </parent>

    <properties>
        <packaging>jar</packaging>
        <jdk.version>11</jdk.version>
        <release.version>11</release.version>
        <micronaut.version>3.7.3</micronaut.version>
        <micronaut.runtime>netty</micronaut.runtime>
        <exec.mainClass>com.sample.app.App</exec.mainClass>
    </properties>

    <repositories>
        <repository>
            <id>central</id>
            <url>https://repo.maven.apache.org/maven2</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-inject</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-validation</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-http-client</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-http-server-netty</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-jackson-databind</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut.test</groupId>
            <artifactId>micronaut-test-junit5</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>io.micronaut.build</groupId>
                <artifactId>micronaut-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <!-- Uncomment to enable incremental compilation -->
                    <!-- <useIncrementalCompilation>false</useIncrementalCompilation> -->

                    <annotationProcessorPaths
                        combine.children="append">
                        <path>
                            <groupId>io.micronaut</groupId>
                            <artifactId>micronaut-http-validation</artifactId>
                            <version>${micronaut.version}</version>
                        </path>
                    </annotationProcessorPaths>

                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

 

Step 3: Define Employee model class.

 

Employee.java

 

package com.sample.app.model;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Positive;

import io.micronaut.core.annotation.NonNull;

public class Employee {

    @NonNull
    @Positive
    private Integer id;

    @NotBlank
    private String name;

    @NonNull
    @Positive
    private Integer age;

    public Employee() {
    }

    public Employee(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + ", age=" + age + "]";
    }

}

 

Step 4: Define EmployeeService class.

 

EmployeeService.java

package com.sample.app.service;

import java.util.ArrayList;
import java.util.List;

import com.sample.app.model.Employee;

import jakarta.inject.Singleton;

@Singleton
public class EmployeeService {
    private static final List<Employee> emps = new ArrayList<>();
    private static final Employee EMP_NOT_FOUND = new Employee();

    private static int counter = 4;

    static {
        Employee emp1 = new Employee(1, "Sunil", 23);
        Employee emp2 = new Employee(2, "Shetty", 31);
        Employee emp3 = new Employee(3, "Ram", 43);
        Employee emp4 = new Employee(4, "Akansha", 21);

        emps.add(emp1);
        emps.add(emp2);
        emps.add(emp3);
        emps.add(emp4);

    }

    public List<Employee> all() {
        return emps;
    }

    public Employee add(final Employee emp) {
        emp.setId(counter);
        counter++;

        emps.add(emp);
        return emp;
    }

    public Employee byId(Integer id) {
        for (Employee emp : all()) {
            if (id.equals(emp.getId())) {
                return emp;
            }
        }
        return EMP_NOT_FOUND;
    }
}

 

Step 5: Define EmployeeController class.

 

EmployeeController.java
package com.sample.app.controller;

import com.sample.app.model.Employee;
import com.sample.app.service.EmployeeService;

import io.micronaut.core.annotation.NonNull;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.PathVariable;
import jakarta.inject.Inject;

@Controller("/employees")
public class EmployeeController {

    @Inject
    private EmployeeService employeeService;

    @Get("/by-id/{employeeId}")
    public Employee byId(@NonNull @PathVariable(name = "employeeId") final Integer empId) {
        return employeeService.byId(empId);
    }
}

Step 6: Define EmployeeControllerTest class.

 

EmployeeControllerTest.java

package com.sample.app.controller;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import com.sample.app.model.Employee;

import io.micronaut.context.ApplicationContext;
import io.micronaut.http.client.HttpClient;
import io.micronaut.runtime.server.EmbeddedServer;

public class EmployeeControllerTest {

    private static EmbeddedServer server;
    private static HttpClient client;

    @BeforeAll
    public static void setupServer() {
        server = ApplicationContext.run(EmbeddedServer.class);
        client = server.getApplicationContext().createBean(HttpClient.class, server.getURL());
    }

    @AfterAll
    public static void stopServer() {
        if (server != null) {
            server.stop();
        }
        if (client != null) {
            client.stop();
        }
    }

    @Test
    public void testIssue() {
        Employee emp = client.toBlocking().retrieve("/employees/by-id/1", Employee.class);

        assertEquals(1, emp.getId());
        assertEquals("Sunil", emp.getName());
        assertEquals(23, emp.getAge());
    }

}

Step 7: Define main application class.

 

App.java

package com.sample.app;

import io.micronaut.runtime.Micronaut;

public class App {

    public static void main(String[] args) {
        Micronaut.run(App.class);
        
        // Use this if you want the beans to be initialized eagerly
        /*Micronaut.build(args)
        .eagerInitSingletons(true) 
        .mainClass(App.class)
        .start();*/
    }
}

Total project structure looks like below.




Build the project using mvn package command.

Navigate to the folder where pom.xml is located and execute the command ‘mvn package’.

 

Upon command successful execution, you can see the jar file ‘micronaut-rest-path-variable-0.1.jar’ in project target folder.

$ ls ./target/
classes
generated-sources
generated-test-sources
maven-archiver
maven-status
micronaut-rest-path-variable-0.1.jar
original-micronaut-rest-path-variable-0.1.jar
surefire-reports
test-classes

 

Execute below command to run the application.

java -jar ./target/micronaut-rest-path-variable-0.1.jar

 

Open the url ‘http://localhost:8080/employees/by-id/1’ to get employee details for the id 1.


You can download this application from this link.


Below table summarizes different path matching patterns.

 

Template

Description

example

/employees/{id}

Simple match

/employees/1

/employees/{id:2}

A variable of two characters max

/employees/10

/employees{/id}

An optional URI variable

/employees/10 or /employees

/employees{/id:[a-zA-Z]+}

 

An optional URI variable with regex

 

/employees/foo

 

/employees/ {?max,offset}

 

Optional query parameters

 

/employees?max=10&offset=10

 

/employees/ {/path:.*}{.ext}

 

Regex path match with extension

 

/employees/foo/bar.xml

 

 

 

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment