これは、なにをしたくて書いたもの?
前にこういったMicroProfile OpenAPIを使ったエントリーを書きました。
WildFly 33とMicroProfile OpenAPI(SmallRye OpenAPI)でOpenAPIドキュメントを生成する - CLOVER🍀
この時は生成したOpenAPIドキュメントをSwagger Editorで確認していたのですが、SmallRye OpenAPIがUIも提供していることがわかったので
こちらを試してみることにしました。
このエントリーでは、差分だけを簡単にまとめます。
SmallRye OpenAPIのUI
SmallRye OpenAPIでは、UIも提供されています。
https://github.com/smallrye/smallrye-open-api/tree/3.10.0/ui
MicroProfile OpenAPIで生成したOpenAPIドキュメントを、Swagger UIで見ることができます。
こちらでビルドした結果を
https://github.com/smallrye/smallrye-open-api/tree/3.10.0/ui/open-api-ui-forms
こちらにJARとしてまとめて使うようです。
https://github.com/smallrye/smallrye-open-api/tree/3.10.0/ui/open-api-ui
こんな感じで、runtime
スコープとして依存関係に追加します。
<dependency> <groupId>io.smallrye</groupId> <artifactId>smallrye-open-api-ui</artifactId> <version>3.10.0</version> <scope>runtime</scope> </dependency>
UIを参照するには/openapi-ui
という固定のパスでアクセスする必要があり、OpenAPIドキュメントは/openapi
でアクセスできる必要が
あります。
どういう仕掛けになっているかというと、smallrye-open-api-ui
にはMETA-INF/resources/openapi-ui
配下にUIのリソースが含まれていて、
こちらをJakarta Servletの仕様で参照します。
$ jar -tvf ~/.m2/repository/io/smallrye/smallrye-open-api-ui/3.10.0/smallrye-open-api-ui-3.10.0.jar 0 Wed Feb 21 00:04:30 JST 2024 META-INF/ 580 Wed Feb 21 00:04:30 JST 2024 META-INF/MANIFEST.MF 0 Wed Feb 21 00:04:30 JST 2024 io/ 0 Wed Feb 21 00:04:30 JST 2024 io/smallrye/ 0 Wed Feb 21 00:04:30 JST 2024 io/smallrye/openapi/ 0 Wed Feb 21 00:04:30 JST 2024 io/smallrye/openapi/ui/ 0 Wed Feb 21 00:04:30 JST 2024 META-INF/resources/ 0 Wed Feb 21 00:04:30 JST 2024 META-INF/resources/openapi-ui/ 0 Wed Feb 21 00:04:30 JST 2024 META-INF/resources/template/ 0 Wed Feb 21 00:04:30 JST 2024 template/ 0 Wed Feb 21 00:04:30 JST 2024 META-INF/maven/ 0 Wed Feb 21 00:04:30 JST 2024 META-INF/maven/io.smallrye/ 0 Wed Feb 21 00:04:30 JST 2024 META-INF/maven/io.smallrye/smallrye-open-api-ui/ 13898 Wed Feb 21 00:04:30 JST 2024 io/smallrye/openapi/ui/IndexHtmlCreator.class 4659 Wed Feb 21 00:04:30 JST 2024 io/smallrye/openapi/ui/Option.class 1389 Wed Feb 21 00:04:30 JST 2024 io/smallrye/openapi/ui/HttpMethod.class 2079 Wed Feb 21 00:04:30 JST 2024 io/smallrye/openapi/ui/ThemeHref.class 931 Wed Feb 21 00:04:30 JST 2024 io/smallrye/openapi/ui/ThemeHref$1.class 1138 Wed Feb 21 00:04:30 JST 2024 io/smallrye/openapi/ui/DocExpansion.class 33496 Tue Sep 05 03:21:24 JST 2023 META-INF/resources/openapi-ui/theme-feeling-blue.css 262590 Fri Feb 16 12:01:22 JST 2024 META-INF/resources/openapi-ui/swagger-ui.css.map 1402103 Fri Feb 16 12:01:20 JST 2024 META-INF/resources/openapi-ui/swagger-ui-bundle.js 6630 Wed Feb 21 00:04:30 JST 2024 META-INF/resources/openapi-ui/logo.png 34635 Tue Sep 05 03:21:24 JST 2023 META-INF/resources/openapi-ui/theme-material.css 2715 Fri Feb 16 12:01:20 JST 2024 META-INF/resources/openapi-ui/oauth2-redirect.html 230809 Fri Feb 16 12:01:20 JST 2024 META-INF/resources/openapi-ui/swagger-ui-standalone-preset.js 33492 Tue Sep 05 03:21:24 JST 2023 META-INF/resources/openapi-ui/theme-flattop.css 356 Wed Feb 21 00:04:30 JST 2024 META-INF/resources/openapi-ui/style.css 1907201 Fri Feb 16 12:01:20 JST 2024 META-INF/resources/openapi-ui/swagger-ui-bundle.js.map 330453 Fri Feb 16 12:01:22 JST 2024 META-INF/resources/openapi-ui/swagger-ui-standalone-preset.js.map 1360 Wed Feb 21 00:04:30 JST 2024 META-INF/resources/openapi-ui/index.html 33490 Tue Sep 05 03:21:24 JST 2023 META-INF/resources/openapi-ui/theme-muted.css 4158 Wed Feb 21 00:04:30 JST 2024 META-INF/resources/openapi-ui/favicon.ico 36042 Tue Sep 05 03:21:24 JST 2023 META-INF/resources/openapi-ui/theme-monokai.css 33471 Tue Sep 05 03:21:24 JST 2023 META-INF/resources/openapi-ui/theme-newspaper.css 33135 Tue Sep 05 03:21:24 JST 2023 META-INF/resources/openapi-ui/theme-outline.css 152042 Fri Feb 16 12:01:20 JST 2024 META-INF/resources/openapi-ui/swagger-ui.css 4595 Wed Feb 21 00:04:30 JST 2024 META-INF/resources/template/index.html 4595 Wed Feb 21 00:04:30 JST 2024 template/index.html 6192 Wed Feb 21 00:03:22 JST 2024 META-INF/maven/io.smallrye/smallrye-open-api-ui/pom.xml 67 Wed Feb 21 00:04:30 JST 2024 META-INF/maven/io.smallrye/smallrye-open-api-ui/pom.properties 299 Wed Feb 21 00:04:30 JST 2024 META-INF/INDEX.LIST
このあたりの話ですね。
The getResource and getResourceAsStream methods take a String with a leading "/" as an argument that gives the path of the resource relative to the root of the context or relative to the META-INF/resources directory of a JAR file inside the web application’s WEB-INF/lib directory. If there is a WEB-INF entry inside the META-INF/resources entry of a JAR file in WEB-INF/lib, then it and all child entries are available only as static resources.
Jakarta Servlet Specification / Servlet Context / Resources
そして、/openapi
からOpenAPIドキュメントを取得してUIを生成するという流れになっています。
環境
今回の環境はこちら。
$ java --version openjdk 21.0.4 2024-07-16 OpenJDK Runtime Environment (build 21.0.4+7-Ubuntu-1ubuntu222.04) OpenJDK 64-Bit Server VM (build 21.0.4+7-Ubuntu-1ubuntu222.04, mixed mode, sharing) $ mvn --version Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 21.0.4, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.15.0-122-generic", arch: "amd64", family: "unix"
前のエントリーの差分追記なので、WildFlyは33.0.2.Finalを使います。
SmallRye OpenAPIのUIを使う
では、SmallRye OpenAPIのUIを使ってみます。Maven依存関係にsmallrye-open-api-ui
をruntime
スコープで追加します。
<dependency> <groupId>io.smallrye</groupId> <artifactId>smallrye-open-api-ui</artifactId> <version>3.10.0</version> <scope>runtime</scope> </dependency>
バージョンは、WildFly 33.0.2.Finalに含まれているSmallRye OpenAPIのバージョンに含まれていました。
で、http://localhost:8080/openapi-ui/
にアクセスすると、Swagger UIを見ることができます。
※作成したソースコードは省略します
よさそうです。
最初からこれで確認すればよかったです…。
オマケ
最初、Webアプリケーションに含めたUIにアクセスできずにかなりハマりました…。
これなのですが、自分が@ApplicationPath
に/
を指定していたのでJAX-RSのルーティングの方に奪われていたからですね。
@ApplicationPath("/") public class RestApplication extends Application { }
なので、割り当てるパスをずらしました…。
@ApplicationPath("/api") public class RestApplication extends Application { }
パスを/
にしたままだと
@ApplicationPath("/") public class RestApplication extends Application { }
以下のようなContainerRequestFilter
を作ればなんとかなりましたが、Content-Type
も指定していませんし、ちょっとイマイチですね…。
src/main/java/org/littlewings/wildfly/openapi/OpenApiUiRequestPassThroughFilter.java
package org.littlewings.wildfly.openapi; import java.io.IOException; import java.io.InputStream; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.servlet.ServletContext; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.container.PreMatching; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.ext.Provider; @PreMatching @Provider @ApplicationScoped public class OpenApiUiRequestPassThroughFilter implements ContainerRequestFilter { @Inject private ServletContext servletContext; @Override public void filter(ContainerRequestContext requestContext) throws IOException { String path = requestContext.getUriInfo().getPath(); if (path.startsWith("/openapi-ui")) { if (path.endsWith("/")) { path = path + "index.html"; } InputStream is = servletContext.getResourceAsStream(path); Response response = Response.ok(is).build(); requestContext.abortWith(response); } } }
<dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <scope>provided</scope> </dependency>
最後に、pom.xml
全体を載せておきましょう。
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>org.littlewings</groupId> <artifactId>microprofile-openapi-ui-example</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.wildfly.bom</groupId> <artifactId>wildfly-ee-with-tools</artifactId> <version>33.0.2.Final</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.wildfly.bom</groupId> <artifactId>wildfly-microprofile</artifactId> <version>33.0.2.Final</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.junit</groupId> <artifactId>junit-bom</artifactId> <version>5.11.2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>jakarta.ws.rs</groupId> <artifactId>jakarta.ws.rs-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>jakarta.enterprise</groupId> <artifactId>jakarta.enterprise.cdi-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>jakarta.validation</groupId> <artifactId>jakarta.validation-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.eclipse.microprofile.openapi</groupId> <artifactId>microprofile-openapi-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>io.smallrye</groupId> <artifactId>smallrye-open-api-ui</artifactId> <version>3.10.0</version> <scope>runtime</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.26.3</version> <scope>test</scope> </dependency> <dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <version>5.5.0</version> <scope>test</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <finalName>ROOT</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.4.0</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.wildfly.plugins</groupId> <artifactId>wildfly-maven-plugin</artifactId> <version>5.0.1.Final</version> <executions> <execution> <id>package</id> <goals> <goal>package</goal> </goals> </execution> </executions> <configuration> <overwrite-provisioned-server>true</overwrite-provisioned-server> <discover-provisioning-info> <version>33.0.2.Final</version> </discover-provisioning-info> </configuration> </plugin> <plugin> <groupId>io.smallrye</groupId> <artifactId>smallrye-open-api-maven-plugin</artifactId> <version>3.10.0</version> <executions> <execution> <goals> <goal>generate-schema</goal> </goals> <phase>compile</phase> </execution> </executions> </plugin> </plugins> </build> </project>
おわりに
SmallRye OpenAPIのUIを試してみました。
自分は余計なところでハマりましたが、ふつうはあっさりと導入できると思うので、MicroProfile OpenAPIを使っているところでUIがない場合は
こちらを使うとよいのかなと思います。