REST-API  -  Swagger in Maven-WebApp einbinden

Hier erfährst Du, wie Du Swagger in eine Maven-WebApp einbindest.

-Welche POM Einträge sind notwendig
Dependencies und Swagger-UI in das Build (WAR) integrieren
-Swagger im Backend "hochziehen"
-openapi.yaml zur Konfiguration von Swagger

INFO:
swagger.version 2.1.6 zieht jackson.*-Abhängigkeiten in der Version 2.11.1. Bis auf eine werden aber alle von resteasy-jackson2-provider "überschrieben" auf 2.11.3 (Stichwort: jackson*)

POM - Einträge

Folgende Dependency-Einträge benötigen wir:

<properties>
    <swagger.version>2.1.6</swagger.version>
    <swagger-ui.version>4.11.1</swagger-ui.version>
    <maven-dependency-plugin.version>3.3.0</maven-dependency-plugin.version>
    <replacer.version>1.5.3</replacer.version>
</properties>
<dependency>
    <groupId>io.swagger.core.v3</groupId>
    <artifactId>swagger-jaxrs2</artifactId>
    <version>${swagger.version}</version>
    <exclusions>
        <exclusion>
            <groupId>javax.ws.rs</groupId>
            <artifactId>jsr311-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>io.swagger.core.v3</groupId>
    <artifactId>swagger-jaxrs2-servlet-initializer-v2</artifactId>
    <version>${swagger.version}</version>
</dependency>

Um Swagger-UI in das WAR zu integrieren, benötigen wir folgende Einträge (die ausgegrauten sollten wir sowieso schon haben, alle anderen werden benötigt):

<build>

<pluginManagement>

    <plugins>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven.compiler.plugin.version}</version>
        <configuration>
            <encoding>${project.build.sourceEncoding}</encoding>
            <source>${maven.compile.source}</source>
            <target>${maven.compile.target}</target>
        </configuration>
    </plugin>

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>buildnumber-maven-plugin</artifactId>
        <version>3.0.0</version>
        <executions>
            <execution>
                <phase>validate</phase>
                <goals>
                    <goal>create</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <doCheck>false</doCheck>
            <doUpdate>false</doUpdate>
            <timestampFormat>{0,date,yyyy-MM-dd HH:mm:ss}</timestampFormat>
        </configuration>
    </plugin>

    <plugin>
        <!-- Download Swagger UI webjar -->
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>${maven-dependency-plugin.version}</version>
        <executions>
            <execution>
                <phase>prepare-package</phase>
                <goals>
                    <goal>unpack</goal>
                </goals>
                <configuration>
                    <artifactItems>
                        <artifactItem>
                            <groupId>org.webjars</groupId>
                            <artifactId>swagger-ui</artifactId>
                            <version>${swagger-ui.version}</version>
                        </artifactItem>
                    </artifactItems>
                    <outputDirectory>${project.build.directory}/swagger-ui</outputDirectory>
                </configuration>
            </execution>
        </executions>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>${maven.war.plugin.version}</version>
        <configuration>
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
            </manifest>
            <warName>YOUR_APP_CONTEXT</warName>
            <archive>
                <manifestEntries>
                    <Specification-Title>${project.name}</Specification-Title>
                    <Specification-Version>${project.version}</Specification-Version>
                    <Implementation-Version>${project.version}</Implementation-Version>
                    <Timestamp>${timestamp}</Timestamp>
                    <BuildNumber>${buildNumber}</BuildNumber>
                </manifestEntries>
            </archive>
            <webResources combine.children="append">
                <resource>
                    <directory>
                        ${project.build.directory}/swagger-ui/META-INF/resources/webjars/swagger-ui/${swagger-ui.version}
                    </directory>
                    <includes>
                        <include>**/*.*</include>
                    </includes>
                    <targetPath>swagger-ui</targetPath>
                </resource>
            </webResources>
            <attachClasses>true</attachClasses>
        </configuration>
    </plugin>

    <plugin>
        <!-- Replace the OpenAPI specification example URL with the local one. -->
        <groupId>com.google.code.maven-replacer-plugin</groupId>
        <artifactId>replacer</artifactId>
        <version>${replacer.version}</version>
        <executions>
            <execution>
                <phase>prepare-package</phase>
                <goals>
                    <goal>replace</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <file>
                ${project.build.directory}/swagger-ui/META-INF/resources/webjars/swagger-ui/${swagger-ui.version}/swagger-initializer.js
            </file>
            <replacements>
                <replacement>
                    <token>https://petstore.swagger.io/v2/swagger.json</token>
                    <value>../openapi/openapi.json</value>
                </replacement>
            </replacements>
        </configuration>
    </plugin>

    </plugins>

</pluginManagement>


<plugins>

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>buildnumber-maven-plugin</artifactId>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
    </plugin>

    <plugin>
        <groupId>com.google.code.maven-replacer-plugin</groupId>
        <artifactId>replacer</artifactId>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
    </plugin>

</plugins>

</build>

Swagger im Backend "hochziehen"

1)
Damit Swagger-UI überhaupt etwas anzeigen kann, muss im Backend Swagger ebenfalls "hochgezogen" werden:

Klassename: CustomOpenApiServlet.java
package de.COMPANY.APPNAME.rest.swagger;

import javax.servlet.annotation.WebServlet;
import io.swagger.v3.jaxrs2.integration.OpenApiServlet;

/**
* Registriert das {@link io.swagger.v3.jaxrs2.integration.OpenApiServlet}, um eine OpenApi-Spezifikation zur Verfügung zu stellen.
*/
@WebServlet(value = "/openapi/*", name = "OpenApiServlet")
public class CustomOpenApiServlet extends OpenApiServlet {
    private static final long serialVersionUID = -8507389627665542354L;
}

2)
Da die Swagger-Implementierung nicht UTF-8-codiert ausliefert, brauchen wir ein Filter:

Klassename: OpenApiFilter.java
package de.COMPANY.APPNAME.rest.swagger;

import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;

/**
* Filtert Requests für das {@link CustomOpenApiServlet}, da die Swagger-Implementierung nicht UTF-8-codiert ausliefert.
*/
@WebFilter(servletNames = "OpenApiServlet")
public class OpenApiFilter implements Filter {

    @Override
    public void doFilter(
        final ServletRequest servletRequest,
        final ServletResponse servletResponse,
        final FilterChain filterChain) throws IOException, ServletException {

        servletResponse.setContentType(APPLICATION_JSON);
        servletResponse.setCharacterEncoding("UTF-8");

        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        // No Objects to destroy
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // Nothing to init
    }
}

3)
Und letztendlich einen SwaggerUI-Resolver für Custom Constraints (composing constraints der Bean Validation JSR-303) welche im Umfeld der WebApp eingesetzt werden. Sorgt dafür, dass die Properties in der Weboberfläche mit den Bean Validation Annotationen korrekt dargestellt wird.

Klassename: CustomPropertyConverter.java

Da die Klasse viel Inhalt hat, gibt es diese zum Download.

Folgendes muss darin angepasst werden:

- wo liegt die Klasse: "package de.COMPANY.APPNAME.rest.swagger;"

- wie beginnen die eigenen Packages: PACKAGES_STARTS_WITH = "de.COMPANY";

openapi.yaml (Konfiguration von Swagger)

Um Swagger zu konfigurieren, kann man eine openapi.yaml Datei in folgendes Verzeichnis legen (dort wo üblicherweise auch eine index.html liegt):

APP-CONTEXT\src\main\webapp\
Dateiname: openapi.yaml
prettyPrint: true
modelConverterClasses: [ 'de.COMPANY.APPNAME.rest.swagger.CustomPropertyConverter' ]
openAPI:
    info:
        title: APPNAME
        version: API-v1
    servers:
        - url: /APP-CONTEXT/api/v1
    description: Aktueller Server

Folgendes muss darin angepasst werden:

- modelConverterClasses: unser "CustomPropertyConverter" wird hier hinterlegt

- APPNAME: der Name der WebApp

- APP-CONTEXT: der Context unter welcher die App auf dem Server läuft (üblicherweise der WAR-File-Name) ("/api/v1" ist hier angegeben, da unsere REST-API mit der Annotation @ApplicationPath("api/v1") "hochgezogen" wird - siehe hier: RestEasy einbinden.