So verwenden Sie den Contract-First-Ansatz mit Open API
Was ist der Contract-First-Ansatz?
Eine Möglichkeit zur Entwicklung Ihrer Dienste, bei der die Spezifikation des Dienstes vor dem Dienst selbst definiert wird. Kurz gesagt: Sie definieren zunächst den Vertrag und implementieren dann den Service.
Was ist Open API?
Laut Swagger,
Die OpenAPI-Spezifikation (OAS) definiert eine standardisierte, sprachunabhängige Schnittstelle zu RESTful-APIs, die es sowohl Menschen als auch Computern ermöglicht, die Funktionen des Dienstes zu entdecken und zu verstehen, ohne Zugriff auf Quellcode, Dokumentation oder eine Überprüfung des Netzwerkverkehrs. Bei richtiger Definition kann ein Verbraucher den Remote-Dienst mit einem minimalen Aufwand an Implementierungslogik verstehen und mit ihm interagieren.
Weitere Informationen finden Sie unter:https://swagger.io/specification/Undhttps://www.openapis.org/
Ein Beispiel für eine offene API-Spezifikation:
Wie generiert man Server- und Client-Code aus OAS?
Sobald Ihr Vertrag definiert ist, ist es an der Zeit, den serverseitigen und clientseitigen Code zu generieren. Wir werden hierfür „ openapi-generator-maven-plugin “ verwenden.
Die Idee besteht darin, einen Vertrag mithilfe der Open API-Spezifikation und zwei APIs zu erstellen: Service und Client .
Die Client-API nutzt die Service-API mithilfe der Clientbibliothek. Wir werden das Openapi-Generator-Maven-Plugin verwenden, um Server- (Schnittstelle, die nur von der Service-API implementiert werden soll) und clientseitigen Code (der von der Client-API zur Nutzung der Service-API verwendet wird) zu generieren. Wir werden das Maven-Profil verwenden, um die Konfigurationen zum Generieren des Server- bzw. Client-Codes anzugeben.
Befolgen Sie die Schritte, um mehr Klarheit zu erhalten:
Erstellen Sie ein Spring-Boot-Maven-Projekt:https://start.spring.io/
Fügen Sie den erstellten Open API-Vertrag zum Ressourcenordner hinzu .
Öffnen Sie pom.xml und fügen Sie die folgenden Eigenschaften und Abhängigkeiten hinzu:
Eigenschaften:
<properties>
<swagger-annotations.version>1.6.0</swagger-annotations.version>
<jackson-databind.version>0.2.1</jackson-databind.version>
<spring-fix.version>3.0.0</spring-fix.version>
<project.build.directory>src/main/resources</project.build.directory>
<server.suffix>server</server.suffix>
<client.suffix>client</client.suffix>
<api-version>v1</api-version>
<base-package>com.oas.contract.first</base-package>
<openapi-generator-maven-plugin.version>5.0.1</openapi-generator-maven-plugin.version>
<inputspec>${project.basedir}/src/main/resources/contract-first.openapi.yaml</inputspec>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.oas.contract.first.sample.server</groupId>
<artifactId>contract-first-server-v1</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- OAS dependencies -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>jackson-databind-nullable</artifactId>
<version>0.2.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-core</artifactId>
<version>${spring-fix.version}</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
</dependencies>
<profiles>
<profile>
<id>server-code</id>
<build>
<plugins>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>${openapi-generator-maven-plugin.version}</version>
<executions>
<execution>
<id>java-server-code-generation</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${inputspec}</inputSpec>
<output>${project.build.directory}/generated-sources/java</output>
<generatorName>spring</generatorName>
<modelPackage>${base-package}.${server.suffix}.model</modelPackage>
<apiPackage>${base-package}.${server.suffix}.api</apiPackage>
<invokerPackage>${base-package}.${server.suffix}.utils</invokerPackage>
<groupId>${project.groupId}.${server.suffix}</groupId>
<artifactId>${project.artifactId}-server-${api-version}</artifactId>
<artifactVersion>${project.version}</artifactVersion>
<configOptions>
<library>spring-boot</library>
<basePackage>${base-package}</basePackage>
<configPackage>
${base-package}.${server.suffix}.config
</configPackage>
<interfaceOnly>true</interfaceOnly>
<unhandledException>true</unhandledException>
<useOptional>true</useOptional>
<useTags>true</useTags>
<hideGenerationTimestamp>true</hideGenerationTimestamp>
<dateLibrary>java8</dateLibrary>
<java8>true</java8>
<booleanGetterPrefix>is</booleanGetterPrefix>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>client-code</id>
<build>
<plugins>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>${openapi-generator-maven-plugin.version}</version>
<executions>
<execution>
<id>java-client-code-generation</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${inputspec}</inputSpec>
<output>${project.build.directory}/generated-sources/java</output>
<generatorName>java</generatorName>
<library>resttemplate</library>
<modelPackage>
${base-package}.${client.suffix}.model
</modelPackage>
<apiPackage>
${base-package}.${client.suffix}.api
</apiPackage>
<invokerPackage>${base-package}.${client.suffix}.utils</invokerPackage>
<groupId>${project.groupId}.${client.suffix}</groupId>
<artifactId>${project.artifactId}-client-${api-version}</artifactId>
<artifactVersion>${project.version}</artifactVersion>
<generateModelTests>false</generateModelTests>
<generateApiTests>false</generateApiTests>
<generateApiDocumentation>false</generateApiDocumentation>
<generateModelDocumentation>false</generateModelDocumentation>
<configOptions>
<basePackage>${base-package}</basePackage>
<configPackage>
${base-package}.${client.suffix}.config
</configPackage>
<hideGenerationTimestamp>true</hideGenerationTimestamp>
<dateLibrary>java8</dateLibrary>
<java8>true</java8>
<booleanGetterPrefix>is</booleanGetterPrefix>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Sobald Sie die folgenden Konfigurationen abgeschlossen haben, führen Sie „ mvn clean install -Pserver-code “ und „ mvn clean install -Pclient-code “ aus.
Ich habe ein Codegenerator.sh- Shell-Skript erstellt, um den Code zu generieren und die Server- und Client-Bibliotheken zu packen.
build_java() {
profile=$1; service=$2
echo "building java packages: $profile $servicename"
(
./mvnw -N -q -P "$profile" clean generate-resources \
"-DartifactName=$service" \
"-Dmaven.test.skip=true" \
|| { echo "cannot generate source code"; exit 0; }
(
cd "target/generated-sources/java" || { echo "cannot cd to client library generated sources folder"; exit 1; }
../../../mvnw -Dmaven.test.skip=true -q clean install
) || exit 0;
) || exit 0
}
Fügen Sie die serverseitige Abhängigkeit zur pom.xml der Service-API hinzu.
Erstellen Sie einen Controller und implementieren Sie die generierte API-Schnittstelle.
Implementieren Sie die Geschäftslogik und führen Sie den Server aus.
Laden Sie die Postman-Sammlung herunter, importieren Sie sie und rufen Sie den Endpunkt „get-service-products“ auf.
Wie verwende ich die generierte Client-Bibliothek?
Wir werden die generierte Clientbibliothek verwenden, um die Service-API zu nutzen.
Erstellen Sie dazu eine weitere Maven-Spring-Boot-Anwendung: Client-API
Fügen Sie die Abhängigkeit der generierten Clientbibliothek in dieser pom.xml hinzu
Erstellen Sie einen Rest-Controller mit Endpunkt: /client/products
Wir müssen den ProductsApi- Client mit der Resttemplate konfigurieren und den Basispfad der Service-API festlegen, wie im Snapshot gezeigt:
Laden Sie nun die Postman-Sammlung herunter, importieren Sie sie und rufen Sie den Endpunkt „get-client-products“ auf.
Dies sollte Ihnen die Liste aller Produkte zurückgeben.
Wie erstellen Sie Veröffentlichungsserver- und Client-Artefakte als Teil Ihres CI/CD-Prozesses?
Wenn Sie die gesamte server- und clientseitige Codegenerierung als Teil Ihres CI/CD- Prozesses durchführen möchten, können Sie das Shell-Skript verwenden und es an Ihre eigenen Bedürfnisse anpassen.
Unser Ziel ist es, sobald der Vertrag entworfen oder geändert wurde, einen Jenkins-Job auszulösen. Dieser Job führt das Codegenerator-Shell-Skript aus, das die Client- und Servercodes generiert, sie verpackt und die Artefakte in Ihr Maven-Repository hochlädt.
Diese Artefakte können dann verwendet werden, indem die Abhängigkeiten in der POM-Datei hinzugefügt werden, wann und wo immer dies erforderlich ist.
Fazit :
Wir haben verstanden, was ein Contract-First-Ansatz und eine offene API-Spezifikation sind. Wir haben zwei APIs erstellt: eine API, die den generierten Servercode implementiert und über die Geschäftslogik verfügt, und eine andere API, die die generierte Clientbibliothek verwendet, um die Service-API zu nutzen. Wir haben auch verstanden, wie wir dies zu einem Teil unseres CI/CD-Prozesses machen können, sodass ein Entwickler die Verträge erstellen/aktualisieren kann und das CI/CD-Tool sich um die Generierung der Bibliotheken kümmern kann.
Den gesamten Code finden Sie auf github .

![Was ist überhaupt eine verknüpfte Liste? [Teil 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































