[JAVA] So erstellen Sie Ihre eigene kopflose API mit Liferays REST Builder (Teil 3)

Einführung

[Teil 1] dieser Serie (https://liferay.dev/blogs/-/blogs/creating-headless-apis-part-1) nutzt das neue REST Builder-Tool von Liferay, um kopflose APIs zu generieren. Startete ein Projekt zu tun. Im Abschnitt Wiederverwendbare Komponenten haben wir die Definitionen der Anforderungs- und Antwortobjekte definiert: die Vitaminkomponente und eine Kopie der Creator-Komponente von Liferay.

In Teil 2 der Serie haben wir die OpenAPI Yaml-Datei durch Definieren von Pfaden (Endpunkten) vervollständigt und den Code trotz häufiger Probleme erfolgreich generiert.

In diesem Teil erfahren Sie, wie Sie Implementierungscode an der entsprechenden Stelle im generierten Code hinzufügen.

Lesen Sie den generierten Code

Es gibt vier Code-generierte Module: "Headless-Vitamin-API", "Headless-Vitamin-Client", "Headless-Vitamin-Impl" und "Headless-Vitamin-Test".

Schauen wir uns jedes Modul einzeln an.

headless-vitamins-api

Das Konzept eines API-Moduls ähnelt dem Service Builder-API-Modul, das eine Schnittstelle für Ressourcen (Dienste) enthält. Es enthält auch spezifische POJO-Klassen für Komponententypen (Vitamin und Creator). Sie sind nicht nur POJOs, die Komponententypklasse verfügt über zusätzliche Setter, die vom Framework beim Deserialisieren von Objekten aufgerufen werden. Schauen wir uns einen der Creator-Komponententypen an.

@JsonIgnore
public void setAdditionalName(
	UnsafeSupplier additionalNameUnsafeSupplier) {

	try {
		additionalName = additionalNameUnsafeSupplier.get();
	}
	catch (RuntimeException re) {
		throw re;
	}
	catch (Exception e) {
		throw new RuntimeException(e);
	}
}

Der oben generierte Code ist sehr einfach, aber keine Sorge.

"VitaminResource" ist die Schnittstelle der Ressource (des Dienstes) und wird aus dem in der OpenAPI Yaml-Datei definierten Pfad entnommen. Nach dem Aufruf von REST Builder stellen Sie möglicherweise fest, dass jedem Pfad von "operationId" in der yaml-Datei neue Attribute hinzugefügt werden und diese Werte genau mit den Methoden der Schnittstelle übereinstimmen.

Der generierte Code allein hat zu wenige Methoden, daher werden wir die Schnittstelle hier teilen.

@Generated("")
public interface VitaminResource {

	public Page getVitaminsPage(
			String search, Filter filter, Pagination pagination, Sort[] sorts)
		throws Exception;

	public Vitamin postVitamin(Vitamin vitamin) throws Exception;

	public void deleteVitamin(String vitaminId) throws Exception;

	public Vitamin getVitamin(String vitaminId) throws Exception;

	public Vitamin patchVitamin(String vitaminId, Vitamin vitamin)
		throws Exception;

	public Vitamin putVitamin(String vitaminId, Vitamin vitamin)
		throws Exception;

	public void setContextCompany(Company contextCompany);

}

Der Pfad / vitamins, der die Sequenz der Vitaminobjekte zurückgibt, ist die erste Methode getVitaminsPage (). Ihre eigene Yaml-Datei deklariert keine PageVitamin-Komponente, sondern eine wird in die exportierte Yaml-Datei eingefügt.

Andere Methoden der Ressourcenschnittstelle stimmen mit den anderen in der Yaml-Datei definierten Pfaden überein. Als nächstes musste ich der build.gradle-Datei des API-Moduls einige Abhängigkeiten hinzufügen:

dependencies {
	compileOnly group: "com.fasterxml.jackson.core", name: "jackson-annotations", version: "2.9.9"
	compileOnly group: "com.liferay", name: "com.liferay.petra.function"
	compileOnly group: "com.liferay", name: "com.liferay.petra.string"
	compileOnly group: "com.liferay", name: "com.liferay.portal.vulcan.api"
	compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel"
	compileOnly group: "io.swagger.core.v3", name: "swagger-annotations", version: "2.0.5"
	compileOnly group: "javax.servlet", name: "javax.servlet-api"
	compileOnly group: "javax.validation", name: "validation-api", version: "2.0.1.Final"
	compileOnly group: "javax.ws.rs", name: "javax.ws.rs-api"
	compileOnly group: "org.osgi", name: "org.osgi.annotation.versioning"
}

Wir haben auch geringfügige Änderungen an der Datei "bnd.bnd" vorgenommen, um die Komponenten und Ressourcenschnittstellen verfügbar zu machen:

Export-Package: com.dnebinger.headless.vitamins.dto.v1_0, \
    com.dnebinger.headless.vitamins.resource.v1_0

headless-vitamins-client

Der Code in diesem Modul erstellt einen Java-basierten Client zum Aufrufen von Headless-APIs.

Der Client-Einstiegspunkt befindet sich in der Klasse <Paketpräfix> .client.resource.v1_0 <Component> Resource. In meinem Fall ist dies die Klasse com.dnebinger.headless.vitamins.client.resource.v1_0.VitaminResource.

Jeder Pfad hat eine statische Methode, jede Methode verwendet dieselben Argumente und gibt dasselbe Objekt zurück. Hinter den Kulissen verwendet jede Methode eine HttpInvoker-Instanz, um den Webdienst unter localhost: 8080 mit [email protected] aufzurufen und Anmeldeinformationen zu testen. Wenn Sie einen Remotedienst testen oder andere Anmeldeinformationen verwenden möchten, müssen Sie die Klasse " Ressource" entsprechend bearbeiten, um unterschiedliche Werte zu verwenden.

Es ist Sache des Designers, die Hauptklasse und anderen Code zu schreiben, um den Client-Code aufzurufen, aber eine vollständige Client-Bibliothek zum Testen ist ein guter erster Schritt!

Das Modul "Headless-Vitamins-Client" hat keine externen Abhängigkeiten, aber Sie müssen das Paket der Datei "bnd.bnd" exportieren.

Export-Package: com.dnebinger.headless.vitamins.client.dto.v1_0, \
    com.dnebinger.headless.vitamins.client.resource.v1_0

headless-vitamins-test

Lassen Sie uns das Modul "Headless-Vitamins-Impl" überspringen und kurz den "Headless-Vitamins-Test" beschreiben.

Der hier generierte Code enthält alle Integrationstests für das Servicemodul und nutzt das Clientmodul zum Aufrufen von Remote-APIs. In diesem Modul haben wir zwei Klassen, "Base ResourceTestCase" und " ResourceTestCase", also "BaseVitaminResourceTestCase" und "VitaminResourceTest".

In der Klasse "VitaminResourceTest" fügen Sie Tests hinzu, die die Klasse "Base" noch nicht implementiert hat. Es ist ein umfangreicher Test zur Verwendung anderer Module und wird zur Fehlerüberprüfung verwendet, wenn versucht wird, einen doppelten Primärschlüssel hinzuzufügen oder ein nicht vorhandenes Objekt zu löschen. Grundsätzlich ist dies ein Test, der nicht einzeln mit einem einfachen Aufruf einer einfachen Ressourcenmethode behandelt werden kann.

Die Datei "build.gradle" für dieses Modul erforderte viele Ergänzungen:


dependencies {
	testIntegrationCompile group: "com.fasterxml.jackson.core", name: "jackson-annotations", version: "2.9.9"
	testIntegrationCompile group: "com.fasterxml.jackson.core", name: "jackson-core", version: "2.9.9"
	testIntegrationCompile group: "com.fasterxml.jackson.core", name: "jackson-databind", version: "2.9.9.1"
	testIntegrationCompile group: "com.liferay", name: "com.liferay.arquillian.extension.junit.bridge", version: "1.0.19"
	testIntegrationCompile group: "com.liferay.portal", name: "com.liferay.portal.kernel"
	testIntegrationCompile project(":modules:headless-vitamins:headless-vitamins-api")
	testIntegrationCompile project(":modules:headless-vitamins:headless-vitamins-client")
	testIntegrationCompile group: "com.liferay", name: "com.liferay.portal.odata.api"
	testIntegrationCompile group: "com.liferay", name: "com.liferay.portal.vulcan.api"
	testIntegrationCompile group: "com.liferay", name: "com.liferay.petra.function"
	testIntegrationCompile group: "com.liferay", name: "com.liferay.petra.string"
	testIntegrationCompile group: "javax.validation", name: "validation-api", version: "2.0.1.Final"
	testIntegrationCompile group: "commons-beanutils", name: "commons-beanutils"
	testIntegrationCompile group: "commons-lang", name: "commons-lang"
	testIntegrationCompile group: "javax.ws.rs", name: "javax.ws.rs-api"
	testIntegrationCompile group: "junit", name: "junit"
	testIntegrationCompile group: "com.liferay.portal", name: "com.liferay.portal.test"
	testIntegrationCompile group: "com.liferay.portal", name: "com.liferay.portal.test.integration"
}

Einige dieser Abhängigkeiten sind die Standardeinstellungen, die nur für Klassen (Junit- und Liferay-Testmodule) benötigt werden, andere hängen vom Projekt ab (Client- und API-Module und möglicherweise andere Module). Es kann einige Versuche und Irrtümer erfordern, um eine Liste zu erhalten, die Ihren Anforderungen entspricht.

Die Datei bnd.bnd für dieses Modul exportiert keine Klassen oder Pakete und musste nicht geändert werden.

headless-vitamins-impl

Es wird endlich interessant. Dies ist das Modul, das den Implementierungscode enthält. REST Builder hat viel Startercode generiert. Mal sehen, wie es aussieht.

com.dnebinger.headless.vitamins.internal.graphql, GraphQL ist da! Die kopflose Implementierung enthält GraphQL-Endpunkte, die Abfragen und Mutationen basierend auf definierten Pfaden verfügbar machen. Beachten Sie, dass GraphQL Abfragen und Mutationsänderungen behandelt, indem es " Resource" direkt aufruft, anstatt einfach Aufrufe an die REST-Implementierung zu übertragen, die üblicherweise in dieser Art von Mischung zu finden ist. .. Daher können Sie GraphQL automatisch nur mit REST Builder abrufen.

com.dnebinger.headless.vitamins.internal.jaxrs.application, hier wird die JAX-RS-Anwendungsklasse gespeichert. Es enthält nichts besonders Interessantes, registriert jedoch die Anwendung im OSGi-Container von Liferay.

com.dnebinger.headless.vitamins.internal.resource.v1_0, hier nehmen Sie Ihre Codekorrekturen vor.

Die Klasse "OpenAPIResourceImpl.java" ist beispielsweise der Pfad zum Zurückgeben der OpenAPI-Yaml-Datei zum Laden in Swagger Hub. Holen Sie sich für jede Schnittstelle " Resource" die abstrakte Basisklasse "Base ResourceImpl" und die konkrete Klasse " ResourceImpl", um die Arbeit zu erledigen. Daher gibt es zwei Klassen: "BaseVitaminResourceImpl" und "VitaminResourceImpl".

Wenn Sie sich die Methoden in der Basisklasse ansehen, sehen Sie, dass sie stark mit Swagger- und JAX-RS-Anmerkungen verziert sind. Werfen wir einen Blick auf eine der Methoden "getVitaminsPage ()", mit denen eine Reihe von in "/ vitamins" gespeicherten Vitaminkomponenten zurückgegeben werden:

@Override
@GET
@Operation(
  description = "Retrieves the list of vitamins and minerals. Results can be paginated, filtered, searched, and sorted."
)
@Parameters(
  value = {
    @Parameter(in = ParameterIn.QUERY, name = "search"),
    @Parameter(in = ParameterIn.QUERY, name = "filter"),
    @Parameter(in = ParameterIn.QUERY, name = "page"),
    @Parameter(in = ParameterIn.QUERY, name = "pageSize"),
    @Parameter(in = ParameterIn.QUERY, name = "sort")
  }
)
@Path("/vitamins")
@Produces({"application/json", "application/xml"})
@Tags(value = {@Tag(name = "Vitamin")})
public Page<Vitamin> getVitaminsPage(
    @Parameter(hidden = true) @QueryParam("search") String search,
    @Context Filter filter, @Context Pagination pagination,
    @Context Sort[] sorts)
  throws Exception {

  return Page.of(Collections.emptyList());
}

Wie ist das?

Dies ist einer der Vorteile, die REST Builder für uns bietet. Alle Anmerkungen werden in der Basisklasse definiert, sodass Sie sich keine Sorgen machen müssen.

Werfen wir einen Blick auf die return-Anweisung, die "Page.of (Collections.emptyList ())" übergibt. Dies ist die Stub-Methode, die von der Basisklasse bereitgestellt wird. Es bietet keine wertvolle Implementierung, stellt jedoch sicher, dass ein Wert zurückgegeben wird, wenn er nicht implementiert wird.

Wenn Sie bereit sind, diese Methode zu implementieren, fügen Sie der Klasse "VitaminResourceImpl" die folgende Methode hinzu (derzeit leer):

@Override
public Page<Vitamin> getVitaminsPage(String search, Filter filter, Pagination pagination, Sort[] sorts) throws Exception {
  List<Vitamin> vitamins = new ArrayList<Vitamin>();
  long totalVitaminsCount = ...;

  // write code here, should add to the list of Vitamin objects

  return Page.of(vitamins, Pagination.of(0, pagination.getPageSize()), totalVitaminsCount);
}

Beachten Sie, dass keine Anmerkungen vorhanden sind.

Wie bereits erwähnt, sind alle Anmerkungen in der überschreibenden Methode enthalten, sodass alle Konfigurationen fertig sind! Daher wird im Gegensatz zu dem von Service Builder generierten Code der Kommentar "Diese Datei wurde generiert, aber bitte ändern Sie diese Datei nicht" nirgendwo angezeigt. Wenn Sie REST Builder erneut ausführen, wird "@Generated (" ")" als Anmerkung für alle (neu) generierten Klassen angezeigt.

Die Klasse "Base ResourceImpl" wird wie folgt kommentiert: Dies ist eine Datei, die jedes Mal neu generiert wird, wenn Sie REST Builder ausführen. Ändern Sie daher nicht die Anmerkungen oder Methodenimplementierungen in dieser Datei. Nehmen Sie alle Änderungen an der Klasse " ResourceImpl" vor.

Wenn Sie die Anmerkungen ändern müssen (** nicht empfohlen **), können Sie dies mit der ResourceImpl-Klasse ` tun und die Anmerkungen aus der Basisklasse überschreiben. Daher müssen Sie der Datei "build.gradle" einige Abhängigkeiten hinzufügen. Meine Datei sieht folgendermaßen aus:

buildscript {
	dependencies {
		classpath group: "com.liferay", name: "com.liferay.gradle.plugins.rest.builder", version: "1.0.21"
	}

	repositories {
		maven {
			url "https://repository-cdn.liferay.com/nexus/content/groups/public"
		}
	}
}

apply plugin: "com.liferay.portal.tools.rest.builder"

dependencies {
	compileOnly group: "com.fasterxml.jackson.core", name: "jackson-annotations", version: "2.9.9"
	compileOnly group: "com.liferay", name: "com.liferay.adaptive.media.api"
	compileOnly group: "com.liferay", name: "com.liferay.adaptive.media.image.api"
	compileOnly group: "com.liferay", name: "com.liferay.headless.common.spi"
	compileOnly group: "com.liferay", name: "com.liferay.headless.delivery.api"
	compileOnly group: "com.liferay", name: "com.liferay.osgi.service.tracker.collections"
	compileOnly group: "com.liferay", name: "com.liferay.petra.function"
	compileOnly group: "com.liferay", name: "com.liferay.petra.string"
	compileOnly group: "com.liferay", name: "com.liferay.portal.odata.api"
	compileOnly group: "com.liferay", name: "com.liferay.portal.vulcan.api"
	compileOnly group: "com.liferay", name: "com.liferay.segments.api"
	compileOnly group: "com.liferay.portal", name: "com.liferay.portal.impl"
	compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel"
	compileOnly group: "io.swagger.core.v3", name: "swagger-annotations", version: "2.0.5"
	compileOnly group: "javax.portlet", name: "portlet-api"
	compileOnly group: "javax.servlet", name: "javax.servlet-api"
	compileOnly group: "javax.validation", name: "validation-api", version: "2.0.1.Final"
	compileOnly group: "javax.ws.rs", name: "javax.ws.rs-api"
	compileOnly group: "org.osgi", name: "org.osgi.service.component", version: "1.3.0"
	compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations"
	compileOnly group: "org.osgi", name: "org.osgi.core"
	compileOnly project(":modules:headless-vitamins:headless-vitamins-api")
}

Sie müssen der Datei bnd.bnd nichts hinzufügen, da alle Pakete intern sind.

Zusammenfassung

Sie haben den Punkt erreicht, an dem Sie mit dem Aufbau Ihrer Implementierung beginnen können! Dieses Mal werde ich es abschneiden.

In Teil 1 haben wir ein Projekt erstellt, wiederverwendbare Komponenten definiert und OpenAPI Yaml angesprochen. Es war. Fügen Sie in Teil 2 alle Pfaddefinitionen für den OpenAPI-Dienst hinzu und verwenden Sie den REST Builder zum Codieren Generiert. In Teil 3 (dieser Artikel) haben wir uns den gesamten generierten Code angesehen und festgestellt, dass wir uns nicht um Codeänderungen oder Anmerkungen zum Implementierungscode kümmern müssen.

Schließlich haben wir im letzten Nächsten Teil dem Projekt das Service Builder-Modul zur Datenspeicherung und alles hinzugefügt Implementieren Sie die Ressourcenmethode von und verwenden Sie den Code "ServiceBuilder".

Wir sehen uns wieder!

https://github.com/dnebing/vitamins

Recommended Posts

So erstellen Sie Ihre eigene kopflose API mit Liferays REST Builder (Teil 3)
So erstellen Sie Ihre eigene kopflose API mit Liferays REST Builder (Teil 2)
So erstellen Sie Ihre eigene kopflose API mit Liferays REST Builder (Teil 4)
So erstellen Sie Ihre eigene kopflose API mit Liferays REST Builder (Teil 1)
So erstellen Sie mit Spring Boot einen eigenen Controller, der / error entspricht
So erstellen Sie Ihre eigene Anmerkung in Java und erhalten den Wert
Erstellen wir eine REST-API mit WildFly Swarm.
[Rails] So erstellen Sie ein Diagramm mit lazy_high_charts
So implementieren Sie optimistisches Sperren in der REST-API
So erstellen Sie hierarchische Kategoriedaten mithilfe von Vorfahren
So erstellen Sie Docker-Compose
[Forge] So registrieren Sie Ihre eigene Entität und Ihr Entity Render in 1.13.2
So erstellen Sie ein Platzhalterteil zur Verwendung in der IN-Klausel
So stellen Sie jQuery in Rails-Apps mit Webpacker bereit
So erstellen Sie ein Service Builder-Portlet in Liferay 7 / DXP
So spielen Sie eine MIDI-Datei mit der Java Sound API ab
Verwendung der Ketten-API
Verwendung von @Builder (Lombok)
Erstellen Sie Ihre eigenen Java-Anmerkungen
Einführung in die EHRbase 2-REST-API
So erstellen Sie eine Methode
So autorisieren Sie mit graphql-ruby
So erstellen Sie eine JAR-Datei und eine War-Datei mit dem Befehl jar
[Rails 6] So erstellen Sie mit cocoon einen dynamischen Formular-Eingabebildschirm
Einfache Möglichkeit zum Erstellen einer Zuordnungsklasse bei Verwendung der API