Ćwiczenie — tworzenie aplikacji Quarkus
W tej lekcji utworzysz podstawową aplikację Quarkus. Narzędzie Maven służy do uruchamiania aplikacji i wybranego zintegrowanego środowiska projektowego (IDE), aby edytować kod. Użyj środowiska powłoki Bash skonfigurowanego w sekcji Wymagania wstępne, aby uruchomić kod. Użyj platformy Docker, aby uruchomić lokalną bazę danych PostgreSQL, aby można było uruchamiać i testować aplikację lokalnie.
Generowanie aplikacji Quarkus przy użyciu narzędzia Maven
Istnieje kilka sposobów generowania struktury projektu Quarkus. Możesz użyć interfejsu internetowego Quarkus, wtyczki IDE lub wtyczki Quarkus Maven. Użyjmy wtyczki Maven, aby wygenerować strukturę projektu.
Aplikacja jest generowana z kilkoma zależnościami:
- Zależność
resteasyuwidacznia punkt końcowy REST - Zależność
jacksondo serializacji i deserializacji danych JSON - Zależność
hibernatedo interakcji z bazą danych - Zależność
postgresqldo nawiązania połączenia z bazą danych PostgreSQL - Zależność
quarkus-container-image-dockerdo zbudowania obrazu Docker
Nie musisz określać zależności platformy Azure, ponieważ uruchamiasz aplikację lokalnie, a następnie wdrażasz konteneryzowaną wersję tej aplikacji w usłudze Azure Container Apps.
Notatka
Polecenia w tym module używają składni powłoki Bash. Nie wklejaj ich bez zmian do programu PowerShell ani do Wiersza polecenia systemu Windows. Na Windows uruchom polecenia powłoki Bash w skonfigurowanej dystrybucji WSL. Użyj innego lokalnego terminalu powłoki Bash dla żądań HTTP, gdy aplikacja jest uruchomiona.
Przed wygenerowaniem projektu sprawdź, czy środowisko powłoki Bash używa Java 17 i maven 3.9.9:
java -version
mvn --version
Początkowe polecenie generujące używa zainstalowanego narzędzia Maven. Wrapper Mavena projektu zostanie utworzony później, więc nie zastępuje tego wymagania wstępnego. Przed kontynuowaniem wybierz wymagane wersje Java i Maven.
W katalogu, w którym chcesz utworzyć projekt, wygeneruj aplikację to-do:
mvn -U io.quarkus:quarkus-maven-plugin:3.19.0:create \
-DplatformVersion=3.18.4 \
-DprojectGroupId=com.example.demo \
-DprojectArtifactId=todo \
-DclassName="com.example.demo.TodoResource" \
-Dpath="/api/todos" \
-DjavaVersion=17 \
-Dextensions="resteasy-jackson, hibernate-orm-panache, jdbc-postgresql, io.quarkus:quarkus-container-image-docker"
To polecenie tworzy nowy projekt Quarkus w katalogu o nazwie todo. Generuje on strukturę katalogów Maven (src/main/java dla kodu źródłowego i src/test/java do testów). Tworzy ona niektóre klasy języka Java, niektóre testy i niektóre pliki Dockerfile. Generuje również plik pom.xml ze wszystkimi wymaganymi zależnościami (Hibernate, RESTEasy, Jackson, PostgreSQL i Docker):
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-docker</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Notatka
Wszystkie zależności w pliku pom.xml są zdefiniowane w Quarkus BOM (bill of materials) io.quarkus.platform:quarkus-bom.
Kod aplikacji
Następnie zmień nazwę wygenerowanej klasy MyEntity.java na Todo.java (znajdującej się w tym samym folderze co plik TodoResource.java ). Zastąp istniejący kod następującym kodem Java. Używa on interfejsu API trwałości języka Java (pakietjakarta.persistence.*) do przechowywania i pobierania danych z serwera PostgreSQL. Używa również Hibernate ORM z Panache (dziedzicząc po io.quarkus.hibernate.orm.panache.PanacheEntity), aby uprościć warstwę persistence.
Do mapowania obiektu Java @Entity bezpośrednio do tabeli PostgreSQL Todo używa się encji JPA (Todo). Punkt końcowy REST TodoResource następnie tworzy nową klasę jednostek Todo i utrwala ją. Ta klasa jest modelem domeny mapowanym na tabelę Todo. Tabela jest tworzona automatycznie przez jpa.
Rozszerzanie PanacheEntity umożliwia szereg ogólnych metod tworzenia, odczytu, aktualizacji i usuwania (CRUD) dla danego typu. Możesz więc wykonywać takie czynności, jak zapisywanie i usuwanie obiektów Todo w jednym wierszu kodu Java.
Dodaj następujący kod do jednostki Todo:
package com.example.demo;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.Entity;
import java.time.Instant;
@Entity
public class Todo extends PanacheEntity {
public String description;
public String details;
public boolean done;
public Instant createdAt = Instant.now();
@Override
public String toString() {
return "Todo{" +
"id=" + id + '\'' +
", description='" + description + '\'' +
", details='" + details + '\'' +
", done=" + done +
", createdAt=" + createdAt +
'}';
}
}
Aby zarządzać tą klasą, zaktualizuj TodoResource, aby umożliwić publikowanie interfejsów REST w celu przechowywania i pobierania danych przy użyciu protokołu HTTP. Otwórz klasę TodoResource i zastąp kod następującym kodem:
package com.example.demo;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;
import jakarta.ws.rs.core.UriInfo;
import org.jboss.logging.Logger;
import java.util.List;
@Path("/api/todos")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
public class TodoResource {
@Inject
Logger logger;
@Inject
UriInfo uriInfo;
@POST
@Transactional
public Response createTodo(Todo todo) {
logger.info("Creating todo: " + todo);
Todo.persist(todo);
UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder().path(todo.id.toString());
return Response.created(uriBuilder.build()).entity(todo).build();
}
@GET
public List<Todo> getTodos() {
logger.info("Getting all todos");
return Todo.listAll();
}
@GET
@Path("/{id}")
public Todo getTodo(@PathParam("id") Long id) {
Todo todo = id == null ? null : Todo.findById(id);
if (todo == null) {
throw new NotFoundException();
}
return todo;
}
}
Metoda POST zwraca URI nowego elementu w nagłówku Location. Metoda GET elementu obsługuje ten URI i zwraca HTTP 404, jeśli element nie zostanie znaleziony. Zabezpieczenie przed null obsługuje również wartości parametrów ścieżki konwertowane przez interfejs RESTEasy na identyfikator null bez przekazywania wartości null do rozwiązania Panache.
Uruchamianie aplikacji
Po uruchomieniu aplikacji w trybie programowania platforma Docker musi być uruchomiona. Dzieje się tak, ponieważ aplikacja Quarkus wykrywa, że potrzebna jest baza danych PostgreSQL (ze względu na zależność quarkus-jdbc-postgresql PostgreSQL zadeklarowaną w pliku pom.xml ), pobiera obraz platformy Docker postgreSQL i uruchamia kontener z bazą danych. Następnie automatycznie tworzy tabelę Todo w bazie danych.
Notatka
Przypięta platforma Quarkus 3.18.4 używa Testcontainers 1.20.4, którego klient Docker Java domyślnie używa API 1.32.
Docker Engine 29.x nie akceptuje już tej wersji interfejsu API domyślnie. Jeśli używasz Engine 29.x, uruchom polecenie docker version i upewnij się, że obsługiwany zakres interfejsów API serwera zawiera 1.44. Tylko jeśli tak jest, uruchom następujące polecenie w tej powłoce powłoki Bash przed uruchomieniem quarkus lub testów:
export JDK_JAVA_OPTIONS="${JDK_JAVA_OPTIONS:+$JDK_JAVA_OPTIONS }-Dapi.version=1.44"
Zachowaj to ustawienie w tej samej powłoce dla poleceń testowych i późniejszych poleceń pakietowania.
JDK_JAVA_OPTIONS przekazuje właściwość systemu Java procesom uruchamiającym Javę bez zmiany przypiętych wersji Quarkus. Samo ustawienie DOCKER_API_VERSION nie konfiguruje tej wersji klienta Docker dla platformy Java. Nie wymuszaj użycia interfejsu API 1.44 na serwerze, którego obsługiwany zakres go nie obejmuje; zamiast tego należy użyć oddzielnie zweryfikowanej zgodnej konfiguracji platformy Docker i klienta.
Upewnij się, że platforma Docker jest uruchomiona i dostępna w skonfigurowanym środowisku powłoki Bash. W terminalu, w którym został wygenerowany projekt, wejdź do katalogu todo i uruchom aplikację:
cd todo
./mvnw quarkus:dev
Uruchom wszystkie kolejne polecenia narzędzia Maven z tego wygenerowanego todo katalogu.
Aplikacja Quarkus powinna uruchomić bazę danych i nawiązać z nią połączenie. Powinny zostać wyświetlone następujące dane wyjściowe:
2025-02-28 08:38:33,418 INFO [io.qua.dat.dep.dev.DevServicesDatasourceProcessor] (build-28) Dev Services for default datasource (postgresql) started - container ID is ce37977203b0
2025-02-28 08:38:33,421 INFO [io.qua.hib.orm.dep.dev.HibernateOrmDevServicesProcessor] (build-6) Setting quarkus.hibernate-orm.database.generation=drop-and-create to initialize Dev Services managed database
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2025-02-28 08:38:35,278 INFO [io.quarkus] (Quarkus Main Thread) todo 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.18.4) started in 5.367s. Listening on: http://localhost:8080
2025-02-28 08:38:35,280 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2025-02-28 08:38:35,280 INFO [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, jdbc-postgresql, narayana-jta, resteasy, resteasy-jackson, smallrye-context-propagation, vertx]
--
Tests paused
Press [e] to edit command line args (currently ''), [r] to resume testing, [o] Toggle test output, [:] for the terminal, [h] for more options>
Aby przetestować aplikację, możesz użyć biblioteki cURL.
W drugim lokalnym terminalu powłoki Bash utwórz nowy element to-do w bazie danych za pomocą następującego polecenia. Powinien zostać wyświetlony dziennik w konsoli Quarkus:
curl --header "Content-Type: application/json" \
--request POST \
--data '{"description":"Take Quarkus MS Learn","details":"Take the MS Learn on deploying Quarkus to Azure Container Apps","done": "true"}' \
http://127.0.0.1:8080/api/todos
To polecenie powinno zwrócić utworzony element (z identyfikatorem):
{"id":1,"description":"Take Quarkus MS Learn","details":"Take the MS Learn on deploying Quarkus to Azure Container Apps","done":true,"createdAt":"2025-02-26T07:27:30.093447Z"}
Utwórz drugą to-do przy użyciu następującego polecenia cURL:
curl --header "Content-Type: application/json" \
--request POST \
--data '{"description":"Take Azure Container Apps MS Learn","details":"Take the ACA Learn module","done": "false"}' \
http://127.0.0.1:8080/api/todos
Następnie pobierz dane przy użyciu nowego żądania cURL:
curl http://127.0.0.1:8080/api/todos
To polecenie zwraca listę elementów to-do, w tym elementów utworzonych przez Ciebie. Każdy element zawiera także createdAt; poniższy przykład pomija te znaczniki czasu dla zwięzłości:
[
{"id":1,"description":"Take Quarkus MS Learn","details":"Take the MS Learn on deploying Quarkus to Azure Container Apps","done":true},
{"id":2,"description":"Take Azure Container Apps MS Learn","details":"Take the ACA Learn module","done":false}
]
Testowanie aplikacji
Po sprawdzeniu protokołu HTTP wróć do terminalu z uruchomioną aplikacją Quarkus i naciśnij klawisze Ctrl+C, aby zatrzymać tryb deweloperski. Pozostaw tę powłokę Bash otwartą i pozostań w katalogu todo dla polecenia testowego.
Aby przetestować aplikację, możesz użyć istniejącej klasy TodoResourceTest. Musi przetestować punkt końcowy REST. Aby przetestować punkt końcowy, używa RESTAssured. Testy zachowują sprawdzanie kolekcji, a następnie podążają za Location zwracanym przez POST i sprawdzają brakujące i nieprawidłowe identyfikatory elementów. Zastąp kod w klasie TodoResourceTest następującym kodem:
package com.example.demo;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.response.Response;
import static io.restassured.RestAssured.given;
import static jakarta.ws.rs.core.HttpHeaders.CONTENT_TYPE;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
@QuarkusTest
class TodoResourceTest {
@Test
void shouldGetAllTodos() {
given()
.when().get("/api/todos")
.then()
.statusCode(200);
}
@Test
void shouldCreateATodo() {
Todo todo = new Todo();
todo.description = "Take Quarkus MS Learn";
todo.details = "Take the MS Learn on deploying Quarkus to Azure Container Apps";
todo.done = true;
Response created = given().body(todo)
.header(CONTENT_TYPE, APPLICATION_JSON)
.when().post("/api/todos")
.then()
.statusCode(201)
.extract().response();
String location = created.getHeader("Location");
assertNotNull(location);
long createdId = created.jsonPath().getLong("id");
Response retrieved = given()
.when().get(location)
.then()
.statusCode(200)
.extract().response();
assertEquals(createdId, retrieved.jsonPath().getLong("id"));
assertEquals(todo.description, retrieved.jsonPath().getString("description"));
}
@Test
void shouldReturnNotFoundForMissingTodo() {
given()
.when().get("/api/todos/-1")
.then().statusCode(404);
}
@Test
void shouldRejectInvalidTodoIds() {
given()
.when().get("/api/todos/not-a-number")
.then().statusCode(404);
given().urlEncodingEnabled(false)
.when().get("/api/todos/%20")
.then().statusCode(404);
}
}
Test tworzenia odczytuje wygenerowany identyfikator z odpowiedzi POST i porównuje oba identyfikatory jako long wartości. Test nieprawidłowego identyfikatora wyłącza automatyczne kodowanie adresów URL dla %20, tak aby żądanie zawierało celowo zakodowaną spację.
Podczas testowania aplikacji platforma Docker musi być uruchomiona i dostępna w tym samym środowisku powłoki Bash, ponieważ aplikacja Quarkus uruchamia oddzielną usługę Dev Service PostgreSQL na potrzeby testowania. W terminalu, w którym zatrzymano aplikację Quarkus, przetestuj aplikację przy użyciu następującego polecenia:
./mvnw clean test
Powinny zostać wyświetlone dane wyjściowe podobne do następujących:
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.demo.TodoResourceTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
Pozostaw tę powłokę Bash otwartą na potrzeby ćwiczenia dotyczącego bazy danych. Ustawisz tam AZ_POSTGRES_USERNAME, AZ_POSTGRES_PASSWORD i AZ_POSTGRES_JDBC_URL, a następnie ponownie uruchomisz aplikację Quarkus z poziomu tej powłoki. Zatrzymaj proces zdalnego tworzenia za pomocą klawiszy Ctrl+C przed późniejszymi ./mvnw clean package i krokami wdrażania. Zachowaj zmienne i nie powtarzaj konfiguracji nazwy zasobu i hasła między modułami.
Proces dziedziczy środowisko podczas uruchamiania. Eksportowanie zmiennych w innym terminalu nie powoduje zaktualizowania jeszcze uruchomionego procesu quarkus. Edytowanie application.properties i poleganie na mechanizmie live reload nie jest wystarczające do przejścia na platformę Azure; uruchom ponownie aplikację z powłoki, w której ustawiono zmienne.