SwaggerをJAX-RS(Jersey)で利用する

# はじめに
RESTful サービスを開発しようと思うと、設計やテストそれに仕様の公開が必要になります。
JAX-RS で開発するプロジェクトで Swagger を活用しようと思います。

# Swagger(OpenAPI) を使うメリット

* 規格化された仕様
* ツールとして利用できる
* **JAX-RS での開発では定義が自動生成できる**

## 仕様としての OpenAPI
RESTful サービスは利用者向けの API のドキュメントも必要です。
なるべく共通の仕様で提供できることが重要です。
そこで、OpenAPI という仕様があります。

https://www.openapis.org/

## 仕様に沿ったツール群
OpenAPI の仕様に沿ったツールが Swagger では豊富にあります。

https://swagger.io/tools/open-source/

設計から、テスト、仕様の公開までをサポートしてくれます。

## JAX-RS と Swagger-Core で定義の自動生成

ここが一番大きいと考えます。
実装からボトムアップで OpenAPI の仕様書を作成できれば非常に便利です。
何故なら、仕様変更の確認と、動作テストがすぐに行えます。

# 構築環境
ここで使う環境は下記の通りです。

* 開発ツールは Eclipse を使う
* JAX-RSの仕様の実装として Jersey を使う
* WEBサーバーは Tomcat を使う

Jersey プロジェクトに Swagger を組み込む方法だけを簡単に説明します。
アプリケーションの機能は単純に「Get it!」を文字列で返すだけの API です。
別途解説しているので、参考にして下さい。

https://qiita.com/edogawara/items/f59d00208d120de1c6c5

参考までに Eclipse の構成としては下記の通りです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834038/85b28d34-b543-97a5-60dd-10930b616e61.jpeg" width="50%">

* Eclipse のプロジェクト名を"jersey2"
* API の実装は、パッケージ "app1" 配下に MyResource.java
* アプリケーションを MyApplication.java
* API のパスを"/webapi"

# Swagger Core を使う

Swagger Core はWEBアプリケーションに組み込む為のモジュールです。

詳しい内容は、下記のリンクを参照して下さい。
Chrome の日本語自動翻訳でも十分理解できる内容です。

https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Getting-started

# Maven の設定
Maven の設定で、以下を追加します。(Tomcat10以上の場合)

```xml:pom.xml
 <dependencies>
    ...
    <dependency>
      <groupId>io.swagger.core.v3</groupId>
      <artifactId>swagger-jaxrs2-jakarta</artifactId>
      <version>2.2.22</version>
    </dependency>
```

:::note info
Tomcat9 以前では swagger-jaxrs2-jakarta ではなく、swagger-jaxrs2 を使う必要があります。
:::

動作としては、JAX-RS 仕様のアノテーション(<span>@</span>:注釈)を読み取って openapi.json または openapi.yaml を自動生成してくれます。
Swagger-UI を使って、生成される URL を参照すれば、定義が視覚化されるという具合です。

## Java EE の名前空間
参考までに、名前空間の各依存性については解りにくいので表にします。

|  JavaEE パッケージ名  | Tomcat | Jersey | Swagger artifactId |
|:-:|:-:|:-:|:-|
|  javax.*   | 9 以前 | 2.x 以前  |  swagger-* |
|  jakarta.* | 10 以降 | 3.0 以降 |   swagger-*-jakarta |

利用する環境によって横のバージョンを合わせないと上手く動きません。

# アプリケーションへの組み込み

アプリケーションに Swagger の解析機能を組み込みます。
一般的な JAX-RS 実装では、

```java:MyApplication.java
@ApplicationPath("/webapi")
public class MyApplication extends Application {
    public Set<Class<?>> getClasses() {
        Set<Class<?>> s = new HashSet<Class<?>>();
        s.add(MyResource.class);
        s.add(OpenApiResource.class);
        return s;
    }
}
```

Jersey に関しては、ResourceConfig を使っても出来ます。

```java:MyApplication.java
@ApplicationPath("/webapi")
public class MyApplication extends ResourceConfig {
    public MyApplication() {
        packages("app1.jersey2"); 
        OpenApiResource openApiResource = new OpenApiResource();
        register(openApiResource);
    }
}
```

# 実行してみる
実行する前に、Tomcat での URL の関係について整理します。

![basepath.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834038/9570cb67-9a8c-c59b-d101-f98d5bb1ab34.jpeg)

<span>@</span>ApplicationPath("/webapi") は、URL の位置では basePath になります。
Tomcat のコンテキストパス("jersey2")は host 名に含まれます。
ですので、実行は、

```
http://localhost:{ポート番号}/{コンテキストパス}/{basePath}/openapi.yaml(または.json)
```

にアクセスすれば定義ファイルが取得できます。
具体的に、Tomcat のポートを 8081 とした場合は以下の通りです。

```
http://localhost:8081/jersey2/webapi/openapi.yaml
```

ブラウザでアクセスするとファイルがダウンロードされるはずです。

# Swagger-UI で参照する
後は、Swagger-UI で先ほどの URL にアクセスすれば良い訳です。
ところが、Swagger-UI を別環境で起動してもそのままでは、CORS の為参照できません。

:::note info
CORS はクロスサイト攻撃をブロックする為にブラウザが持つセキュリティ機能。
:::

## CORS の回避方法 
CORS を回避する方法は複数あります。

* 同じドメイン内で Swagger UI を動かす
* Tomcat で CORS の許可設定を行う
* アプリケーションが CORS を許可する

どれもありだと思いますが、ここでは一つだけ説明します。

## 同じサーバー(Tomcat)上で Swagger-UI を動かす

Swagger-UI はシングルページ・アプリケーションなので設置は簡単です。
最新版をダウンロードします。

https://github.com/swagger-api/swagger-ui

解凍した **dist** フォルダの中身だけを Eclipse プロジェクトの webapps 配下にコピーします。
下はその例です。

![jersey8.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834038/ad5d6460-003c-7950-8821-815e43753a06.jpeg)

プロジェクトを実行し、ブラウザの URL に

```
http://localhost:8081/jersey2/swagger-ui/
```

を指定すれば Swagger-UI が起動できます。
openapi.yaml(またはjson)の URL を指定すれば、

![swagger_sample1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834038/7a13886a-b336-de74-14df-f9ac3876a37c.jpeg)

が表示されます。

# Tomcat のコンテキストパスの扱い
Swagger 上で「Try it out」で実行すると、下記のエラーになります。

![swagger_error1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834038/2315004a-ccce-3f54-925c-7191e67fab98.jpeg)

Swagger は、デフォルトでは Tomcat のコンテキストパスからの相対では無く、サーバー(localhost:8081)からの相対をエンドパスとしてしまう為です。
openapi.yaml(json) にサーバーパスの設定が必要です。

## アノテーションで Swagger の生成定義を指定する
簡単な方法として、Swagger Core のアノテーションを使います。
ここでは、ついでにタイトルやコメントも変更してみましょう。
解り易く MyApplication.java に記述しますが、MyResource.java に書いても構いません。

```java:MyApplication.java
@ApplicationPath("/webapi")
@OpenAPIDefinition(
    info = @io.swagger.v3.oas.annotations.info.Info(
        title = "Jersey Sample API",
        version = "0.0",
        description = "Swagger Core で定義を生成",
        license = @io.swagger.v3.oas.annotations.info.License (name = "Apache 2.0", url = "http://foo.bar"),
        contact = @Contact(url = "http://hoge.com", name = "yamada", email = "yamada@hoge.com")
    ),
    servers = {
        @Server( description = "tomcat10", url = "/jersey2" )
    }
)
```

反映されれば Swagger-UI の　servers に /jersey2 が表示されます。
「Try it out」も今度は問題なく実行できるはずです。

![swagger_jersey1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834038/3089b695-7722-c950-9471-0da420a5192b.jpeg)

# 追加の設定
些細な事ですが、Swagger-UI のデフォルトの URL を変更したいと思う人は多いと思います。
どうも **これだ！** という方法が見つかりません。
将来も適用できるか分かりませんが、面倒なので、
swagger-initializer.js
を直接変更してしまうのが一番簡単そうです。

```javascript:swagger-initializer.js
window.onload = function() {

container
  ：
  window.ui = SwaggerUIBundle({
    // url: "https://petstore.swagger.io/v2/swagger.json",
    url: "http://localhost:8081/jersey2/webapi/openapi.json",
  ：
};
```

:::note warn
変わらない場合、たまにブラウザの JavaScript のキャッシュが残っている場合があるのでクリアして下さい。
:::

# 最後に
当初もっと複雑なサンプルを使うつもりでしたが、基本に戻ってまずは簡単なものにしました。
API を実装していく上で、Swagger でテストできるのは効率が良いです。