rksoftware

Visual Studio とか C# とかが好きです

ASP.NET Core WebAPI を Swashbuckle でドキュメント作って AutoRest でクライアントを生成

OpenAPI / Swagger なんもわかりません。誰か教えて下さい。

なんとなく思い立って ASP.NET Core WebAPI を Swashbuckle でドキュメント作って AutoRest でクライアントを生成してみるチャレンジをしていました。
なにか全然動かなくて手間をかけたので、書き残しておきます。正しい知識でスマートに扱える方、使い方をこっそり教えて下さい。

この記事の続報

後日より簡単にクライアントが作れるようになりました。クライアント生成に関するアップデートは次の記事です。

■ WebAPI を作る

最初はまずドキュメントを作る WebAPI を作ります。.NET Core がインストールされていればコンソールで一行です。

dotnet new webapi

かんたんですね。

■ Swashbuckle.AspNetCore をインストール

Swashbuckle.AspNetCore を NuGet からインストールします。
インストールとコードの追加は次のリンク先をなぞるだけです。

少し手間取った点は2つ

  • GUI での検索では、きちんとフルネーム入力しないと別のパッケージしか出てきませんでした。
  • リリース版の最新版のインストールではビルドできませんでした。バージョンはプレリリース版の 5.0.0-rc5 を入れました。(2019/12/18)

かんたんですね。

■ アプリを起動し swagger.json をダウンロード

アプリを起動(デバッグ実行)します。Visual Studio で起動すると次の URL でブラウザが上がって来ると思います。
http://localhost:5000/weatherforecast
https://localhost:5001/weatherforecast

これを末尾を /swagger と打ち替え次のようなアドレスへ移動します。
http://localhost:5000/swagger
https://localhost:5001/swagger

すると API 仕様が記載されたページが出てきます。出てきたページの 「/swagger/v1/swagger.json」 リンクから swagger.json ファイルをダウンロードします。

ダウンロードした swagger.json は次の様になっていました。

{
  "openapi": "3.0.1",
  "info": {
    "title": "My API",
    "version": "v1"
  },
  "paths": {
    "/WeatherForecast": {
      "get": {
        "tags": [
          "WeatherForecast"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WeatherForecast"
                  },
                  "nullable": true
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WeatherForecast"
                  },
                  "nullable": true
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WeatherForecast"
                  },
                  "nullable": true
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "WeatherForecast": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "temperatureC": {
            "type": "integer",
            "format": "int32"
          },
          "temperatureF": {
            "type": "integer",
            "format": "int32",
            "readOnly": true
          },
          "summary": {
            "type": "string",
            "nullable": true
          }
        },
        "nullable": true
      }
    }
  }
}

かんたんですね。

■ AutoRest をインストール

swagger.json から C# クライアントコードを生成します。今回は AutoRest を使ってみました。

npm でインストールしました。

npm install -g autorest@bata

かんたんですね。

■ AutoRest で C# コードを生成

AutoRest で C# コードを生成します。引数の説明は省略しますがこんな感じで実行できます。

autorest --input-file=swagger.json --csharp --output-folder=CSharp_Swagger --namespace=swagger

かんたんですね。

■ エラーで作られない

前述のコマンドで実行できるのですが、次のエラーで生成されませんでした。

Invalid Version: 1 / TypeError: Invalid Version: 1

これに対して、OpenAPI / Swagger なんもわからないので、swagger.json を書き換えて動かす方針を取りました。

かんたんですね。

■ 出会ったエラーたち

最終的にコードを生成できるまでには次のエラーたちに出会いました。

Invalid Version: 1 / TypeError: Invalid Version: 1
Schema violation: Additional properties not allowed: components
Schema violation: Additional properties not allowed: nullable
Schema violation: Data does not match any schemas from 'oneOf'
FATAL: OperationId is required for all operations. Please add it for 'get' operation of '/WeatherForecast' path. 

かんたんですね。

■ 変更後の swagger.json

最終的にコードを生成できるようになった swagger.json です。意味はわかっていませんが、一旦生成できるようになったので書き残しておきます。後日意味がわかるようになる勉強をするときのために。

{
  "swagger": "2.0",
  "info": {
    "title": "My API",
    "version": "1.0.0"
  },
  "paths": {
    "/WeatherForecast": {
      "get": {
        "tags": [
          "WeatherForecast"
        ],
        "operationId": "getWeatherForecast",
        "responses": {
          "200": {
            "description": "Success",
            "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/definitions/WeatherForecast"
              }
            }
          }
        }
      }
    }
  },
  "definitions": {
      "WeatherForecast": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "temperatureC": {
            "type": "integer",
            "format": "int32"
          },
          "temperatureF": {
            "type": "integer",
            "format": "int32",
            "readOnly": true
          },
          "summary": {
            "type": "string"
          }
        }
      }
  }
}

かんたんですね。