NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

注目のタグ

    Swagger UIを構成する静的リソースの編集方法

    1. はじめに

    皆さんこんにちは、新卒2年目の松澤武志です!
    普段はJavaやAngularのアプリケーション開発を行い、趣味でAWSコンソールをいじっています!

    AWSに関するブログを中心に執筆してきましたが、今回はアプリケーション開発で得られた知見を共有させていただきたいと思います。

    突然ですが、開発タスクでSwagger UIのフロントエンドを修正する必要がありました。私の担当しているシステムは、springdoc-openapi*1を使用してSwagger UIを自動生成するため、静的リソースを直接編集することができませんでした。今回は、そのような制約の中でどのように対応したのかをご紹介します。

    2. Swagger UIとは

    Swaggerに関しては、弊社の井手上さんが丁寧に解説しているので、是非こちらのブログを参照して下さい。

    tech.nri-net.com

    Swagger UIを簡単に説明すると、ブラウザ上でAPIをインタラクティブに操作できるツールです。 開発したAPIエンドポイントの動作確認が手軽にできたり、手動テスト時のデータの確認ができたりします。

    3. Swaggerの静的リソースを編集する方法

    3-1. 必要なライブラリの依存関係注入

    Spring Initializerで新規Spring Bootアプリケーションを作成します。この時、Swagger UIを使用するためにbuild.gradleに下記依存関係を注入します。

    dependencies {
        implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'
    }
    

    springdoc-openapiは、Spring BootアプリケーションにOpenAPI(Swagger)の仕様を統合するためのライブラリです。コントローラークラスやリクエスト・レスポンスの定義から、自動でOpenAPI 3.0ドキュメントを作成してくれます。

    Swagger Editor*2によるドキュメントの作成を行う必要がなく、実装ベースでSwagger UIを自動生成できるので、ドキュメントの運用コストを削減することが可能です。

    3-2. Swaggerに表示させるAPIの作成

    Swaggerに表示させるAPIを作成します。今回はAPIをたたかず、SwaggerのUIを表示させるだけなので、Amazon Q Developer*3に新規作成してもらいました。

    今回はTaskテーブルに存在するタスクを編集するという体でGET、POST、PUT、DELETEメソッドのAPI4つを作成しました。また、HelloWorldを表示させるだけのAPIと合わせて5つ作成しました。

    3-3. Swagger UIの編集

    Swagger UIの静的リソースの編集は下記ドキュメントを参考にしています。

    springdoc.org

    Swagger UIの静的リソースはMETA-INF/resources/webjars/swagger-ui/{swagger.version}/の配下のリソースとなっており、下記のファイルのことを指します。

    • index.html
    • swagger-ui-bundle.js
    • swagger-ui.css
    • swagger-ui-standalone-preset.js
    • swagger-ui.css.map
    • swagger-ui-bundle.js.map
    • swagger-ui-standalone-preset.js.map
    • favicon-32x32.png

    編集するためのコード

    修正用のコード自体は、SwaggerIndexPageTransformerというクラスを継承したSwaggerCodeBlockTransformerと、それを呼び出すOpenApiConfigクラスを実装するだけです。

    package com.example.demo.Transformer;
    
    import jakarta.servlet.http.HttpServletRequest;
    import org.springdoc.core.properties.SwaggerUiConfigParameters;
    import org.springdoc.core.properties.SwaggerUiConfigProperties;
    import org.springdoc.core.properties.SwaggerUiOAuthProperties;
    import org.springdoc.core.providers.ObjectMapperProvider;
    import org.springdoc.webmvc.ui.SwaggerIndexPageTransformer;
    import org.springdoc.webmvc.ui.SwaggerWelcomeCommon;
    import org.springframework.core.io.Resource;
    import org.springframework.web.servlet.resource.ResourceTransformerChain;
    import org.springframework.web.servlet.resource.TransformedResource;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.util.stream.Collectors;
    
    public class SwaggerCodeBlockTransformer
            extends SwaggerIndexPageTransformer {
    
        public SwaggerCodeBlockTransformer(SwaggerUiConfigProperties swaggerUiConfig,
                                           SwaggerUiOAuthProperties swaggerUiOAuthProperties,
                                           SwaggerUiConfigParameters swaggerUiConfigParameters,
                                           SwaggerWelcomeCommon swaggerWelcomeCommon,
                                           ObjectMapperProvider objectMapperProvider) {
            super(swaggerUiConfig, swaggerUiOAuthProperties, swaggerUiConfigParameters, swaggerWelcomeCommon, objectMapperProvider);
        }
    
        @Override
        public Resource transform(HttpServletRequest request,
                                  Resource resource,
                                  ResourceTransformerChain transformer)
                throws IOException {
            // ここで静的リソースを選択する
            if (resource.toString().contains("index.html")) {
                final InputStream is = resource.getInputStream();
                final InputStreamReader isr = new InputStreamReader(is);
                try (BufferedReader br = new BufferedReader(isr)) {
                    final String html = br.lines().collect(Collectors.joining());
                    // ここでリソースの記載を置換する
                    final byte[] transformedContent = html.replace("<title>Swagger UI</title>",  "<title>hoge hoge</title>").getBytes();
                    return  new TransformedResource(resource, transformedContent);
                } // AutoCloseable br > isr > is
            }
            return super.transform(request, resource, transformer);
        }
    }
    

    transformメソッドの中のif文で編集するリソースを選択し、transformedContentでソースの置換を行います。

    package com.example.demo.Config;
    
    import com.example.demo.Transformer.SwaggerCodeBlockTransformer;
    import org.springdoc.core.properties.SwaggerUiConfigParameters;
    import org.springdoc.core.properties.SwaggerUiConfigProperties;
    import org.springdoc.core.properties.SwaggerUiOAuthProperties;
    import org.springdoc.core.providers.ObjectMapperProvider;
    import org.springdoc.webmvc.ui.SwaggerIndexTransformer;
    import org.springdoc.webmvc.ui.SwaggerWelcomeCommon;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class OpenApiConfig {
        @Bean
        public SwaggerIndexTransformer swaggerIndexTransformer(
                SwaggerUiConfigProperties swaggerUiConfig,
                SwaggerUiOAuthProperties swaggerUiOAuthProperties,
                SwaggerUiConfigParameters swaggerUiConfigParameters,
                SwaggerWelcomeCommon swaggerWelcomeCommon,
                ObjectMapperProvider objectMapperProvider) {
            return new SwaggerCodeBlockTransformer(swaggerUiConfig, swaggerUiOAuthProperties,
                    swaggerUiConfigParameters, swaggerWelcomeCommon,objectMapperProvider);
        }
    }
    

    if文で指定する静的リソースファイルに対して、replace内の処理を行います。今回は例として、index.html内に存在する<title>Swagger UI</title><title>hoge hoge</title>に変換するコードを記載してみました。

    また、パッケージを増やしたくない等などの意向があれば、Nested Classを用いてこれらのファイルをSwaggerConfig.java等にまとめて記載しても問題ありません。

    3-4. 起動

    いざ起動してみた結果がこちらになります。

    修正前(タイトルがSwagger UI)

    修正後(タイトルがhoge hoge)

    また、下記のように<meta name="google" value="notranslate">*4を記載すると、google翻訳ボタンを非表示にすることもできます。

    final byte[] transformedContent = html.replace("<meta charset=\"UTF-8\">", "<meta charset=\"UTF-8\"> \n <meta name=\"google\" value=\"notranslate\">").getBytes();
    

    修正後(Google翻訳ボタンの非表示)

    4. まとめ

    今回はSwagger UIの静的リソースの編集方法をまとめました。今回は利用しませんでしたが、swagger-ui.cssfavicon-32x32.png 等の別のリソースも編集することが可能です。

    静的リソース自体を修正する機会がありましたら、是非参考にしてみてください!

    5. あとがき

    今回のTask関係のAPIはAmazon Q Developerによって作成されました。今回は下記プロンプトを入力しました。

    以下の要件に従って4つのREST APIを作成してください。
    まず、Taskテーブルを定義します。タスクテーブルのカラムはid(String)、taskName(String)、task(String)で構成されます。
    タスクテーブルに存在するタスクデータを取得するAPI4つを作成します。それぞれの処理の詳細を記述します。
    GETメソッドは指定したidに関するタスクのtaskNameおよびtaskを返します。
    エンドポイントは「/task/{id}」となり、idはパスパラメータとして指定します。
    POSTメソッドはtaskNameおよびtaskをJSONによるパラメータで新規Taskを入力します。
    エンドポイントは「/task」となります
    PUTメソッドは指定したidのtaskNameおよびtaskをJSONによるパラメータで更新します。
    エンドポイントは「/task/{id}」となり、idはパスパラメータとして指定します。
    DELETEメソッドは指定したidのtaskNameおよびtaskを削除します。
    エンドポイントは「/task/{id}」となり、idはパスパラメータとして指定します。
    作成するクラスは、Controller、Service、Entity、Mapperの4つで、Controllerでリクエストを受け取り、
    Serviceで内容を処理し、Mapperでデータを取得します。TaskテーブルのデータをEntityに記載します。
    それぞれのパッケージはcom\example\demo配下に別々で作成してください。
    

    こちらのプロンプトにより、一括でAPIを4つ新規作成することができました。詳細に記載したため、1つの入力で大幅なコード作成を行ってくれました。

    今回のプロジェクトは以前Amazon Q Developerを利用した時のAPIを参考にして改良しました。こちらの記事もぜひ見てみてください。

    tech.nri-net.com


    執筆者 松澤武志

    2025 Japan AWS Jr. Champion
    趣味でAWSをいじるネイティブアプリエンジニア(.NET/C#/Java/Angular)

    ・執筆記事一覧: https://tech.nri-net.com/archive/author/t-matsuzawa
    ・X:https://x.com/2357_takeshi
    ・Speaker Deck:https://speakerdeck.com/matsuzawatakeshi
    ・Qiita:https://qiita.com/takeshi18