NRIネットコム Blog

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. Champions
趣味でAWSコンソールをいじるWebアプリケーションエンジニア(Java/C#/Angular/Vue.js)
執筆記事一覧: https://tech.nri-net.com/archive/author/t-matsuzawa