BEACHSIDE BLOG

Azure とか C# 好きなエンジニアの個人メモ ( ・ㅂ・)و ̑̑

ASP.NET Core で Open API ( Swagger ) の設定の基礎 (.NET Core 3.1 と .NET 5)

OpenAPI の tool set である Swagger の設定、しばらくしてなかったので改めてメモしていきます。

まず余談ですが、ASP.NET Core の Web API で Swagger を利用するときに使う NuGet package は、現時点では主にこの2つって感じです。

GitHub を見ると Swashbuckle は利用数が NSwag に比べてめちゃ多いですが、開発がしばらく止まってる感じでむむーって感じでした。2020年に開発が徐々に活発になり復活してる感あります。
NSwag は、Swashbuckle と同等の機能に加え、クライアント側( Web API をたたく側) のコードを C# や Typescript で生成するツールがあります。また、開発もガンガン進んでいて Star 数も気づけば Swashbuckle を追い抜いておりいい感じの勢いを感じます。

どっちがいいのかって言われると (個人的には)好きな方使えばと感じるところです。
今回は ASP.NET Core 5.0 でテンプレートにも組み込まれた Swashbuckle で行きます。

ちなみに NSwag は以前にしばやん先生がめちゃ詳しくまとめてます。
ASP.NET Core Web API と NSwag を使って OpenAPI 定義を自動生成する - しばやん雑記


さて本題ですが、
今回は ASP.NET Core 3.1 と 5.0 で Web API を実装する想定でみていきます。3.1 と 5.0 とゆーてますが設定方法は基本的に一緒です。

ゴールはこの二つです。

  • Swagger をセットアップして Swagger UI を使って API を見てコールできる
  • Swagger UI を、認証が必要な API に対応させる

1. Swagger の導入設定(Swashbuckle)

Visual Studio 2019 にて ASP.NET Core 3.1 で ASP.NET Core の Web API を作成したときに生成されるデフォルトのコード (WeatherForecastController が自動生成されるコード) を手を加えてみましょう。

というのも、ASP.NET Core 5.0ASP.NET Core Web API を作ると、テンプレートで Enable OpenAPI support ってチェックボックスがついます。チェックをオンにするだけで、コードが生成されてしまうのでなにもせずに導入ができてしまうため、導入の設定ではかくことがありません。ただ、シンプルな設定なので後述の内容は自分で設定する必要がありますが。

f:id:beachside:20210113163827p:plain

ASP.NET Core 3.1 以前のバージョンでは Enable OpenAPI support のチェックボックスはないので自分で導入する必要があります。

ちなみに Swashbuckle.AspNetCore のバージョンは現時点で最新の v5.6.3 を使っています。

NuGet package のインストール

Swashbuckle.AspNetCore をインストールしておけばよいです。

f:id:beachside:20210114185308p:plain

Startup.cs の編集

以下は ASP.NET Core 5.0 を作ったときに生成された Startup.cs のコードです。.ASP.NET Core 3.1 の場合だと、前述の NuGet をインストールして、以下のコード同様に Swagger に関するコードを追加すればよいです。

まずは 23-26 行目ですね。

そして 34-35行目。Environments が "Develop" の時のみ Swagger UI を出力するように設定しています。ケースによってはこんなこともあるかもしれませんね。

if (!env.IsProduction())
{
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "SwaggerSandbox.AspnetCore50Sample v1"));
}

ここではあまり関係ない余談になりますが、 Environments については以下のドキュメントに書かれています。GenericHost が導入されてから(だっけ?) DOTNET_ENVIRONMENT とそれをオーバーライドする ASPNETCORE_ENVIRONMENT の2段構成になりましたねーってふと思い出しました。

ASP.NET Core で複数の環境を使用する | Microsoft Docs

デバッグ実行

ここまででデバッグ実行して Swagger UI を見てみましょう。https://localhost:5001/ でデバッグしてる場合、デフォルトの設定だと以下のURL で Swagger UI にアクセスできます。

  • https://localhost:5001/swagger

f:id:beachside:20210114194249p:plain

GET の "/WeatherForecast" を試してみましょう。Try it out ボタンをクリック > Execute をクリックすると API をコールすることができます。結果がその下に表示されます。

f:id:beachside:20210114194333p:plain


ここまでが ASP.NET Core 5.0 のデフォルトでの設定です。以降でデフォルトではされてない設定をしていきます。

2. デバッグ開始時に Swagger UI を起動する

デバッグを開始する度に手作業で Swagger UI を開いてるのはくそめんどいので、起動時に開くようにしておきましょう。ソリューションエクスプローラーで Properties > launchSettings.json を開きます。

f:id:beachside:20210114204705p:plain

デバッグする profile の設定で以下の2つを設定します。起動時にブラウザーを立ち上げるってのと、開くページの相対パスの設定です。

  • "launchBrowser": true
  • "launchUrl": "swagger"

下図では二箇所の赤枠をつけてますが、IIS Express or Kestrel のデバッグ起動する方をだけを設定すればよいです。

f:id:beachside:20210114204444p:plain

これでデバッグ時にブラウザーで Swagger UI が起動するようになりました。

3. Swagger UI の各 API にコメントを追加

.NET 界隈ではコードに XML コメント (triple slash comments) を書くことがありますよね。これを Swagger UI に反映させます。

WeatherForecastController の編集

Swagger UI にコードの XML コメントを表示させるためにちょっとコードを追加します。まず、Controllers フォルダの WeatherForecastController を開きます。

f:id:beachside:20210114195814p:plain

サンプルとしてこんなメソッドを追加します。XML コメントも追加しておきました。 スラッシュ3つ /// を入力するとスニペットでドカッと入力されるやつですね。
remarks はコードハイライトさせたくて書いただけの意味のないコメントです。/// のうしろって基本的にスペースを1つ入れて文字を書きますが、そこからさらに4つスペースをつけて (つまり /// の後に5つスペースをいれて) 文字を書くとコードハイライトされます。POST の処理で Body にサンプルのデータを JSON で書きたいときとかいいですね。

        /// <summary>
        /// 指定したカウントだけサンプルの天気予報データを返します。
        /// </summary>
        /// <remarks>
        ///  スペースを 1+4つ先頭につけると、コードハイライトされます。
        ///
        ///     {
        ///       "id": 1,
        ///       "name": "Item1",
        ///       "isComplete": true
        ///     }
        /// </remarks>
        /// <param name="count">天気予報データの件数 (1件以上、100件未満)</param>
        /// <returns>天気予報データ</returns>
        [HttpGet("Get2")]
        public ActionResult<IEnumerable<WeatherForecast>> Get2(int count)
        {
            if (count < 1 || count > 100)
            {
                return BadRequest();
            }

            var rng = new Random();
            return Enumerable.Range(1, count).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
                .ToArray();
        }

Swagger に取り込む

Swagger に取り込むってのは雑な表現ですが、もう少し具体的に書くと以下の2つをやります。

  1. XML コメントのドキュメントを出力するために csproj の編集
  2. 出力された XML コメントのドキュメントを Swagger 生成時に読み込むために Startup.cs の編集


まずは1つめ、XML コメントのドキュメントを出力するするのに csproj を編集します

ソリューションエクスプローラーでプロジェクト名をクリックして csproj ファイルを開きます。

f:id:beachside:20210114201616p:plain

以下は csproj の全体ですが、追加したのは PropertyGroup の中にある以下の2つの要素です。

  • GenerateDocumentationFile : XML コメントのファイルを出力するように設定しています。
  • NoWarn : GenerateDocumentationFile を true に設定すると、XML コメントがついてない public なメソッドは全て警告が出ます。鬼のようにでるので、その警告を出力させない場合はこの設定をします。
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <NoWarn>$(NoWarn);1591</NoWarn>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
  </ItemGroup>

</Project>


次は2つめ、出力された XML コメントのドキュメントを Swagger 生成時に読み込むために Startup.cs の編集します

Startup.cs を開きましょう。コメント「// ここを追加」って書かれてるところより下の3行を追加しました (適宜 using ステートメントも追加してます)。これで出力した xml コメントのファイルを Swagger くんが取り込むようになります。

using System;
using System.IO;
using System.Reflection;

※省略

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "SwaggerSandbox.AspnetCore50Sample", Version = "v1" });

                // ここを追加
                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                c.IncludeXmlComments(xmlPath);
            });
        }

※省略

デバッグ実行

デバッグ実行して、Swagger UI を開いてみましょう。以下の赤枠のところが XML コメントにより追加された部分になります。

f:id:beachside:20210114210128p:plain

認証に対応する (簡易版)

認証・認可で保護された Web API ってのはよくあると思いますOAuth2 や OIDC 対応の認証プロバイダーを使っていて、クライアント側からリクエストを送信する際に Authorization ヘッダーに Bearerトークンをつけてリクエストを送信する必要がある API ってあるあるですよね。ここでは簡易な設定として、Bearer トークンをセットできるよう設定していきます。

Swagger UI から Azure AD で認証してトークンを取得する記事は下の方にリンク張ってます。♪

具体的な IdP だとAuth0 や Azure Active Directory、Azure Active Directory B2C を認証に使ってる時とかですね。

Swagger UI に以下図のような Authorize ボタンを配置して Bearer トークンをセットできるように変更してみます。

f:id:beachside:20210120171822p:plain

これをやるやら ASP.NET Core に認証の実装をする必要があるのですがその実装は本題じゃないので省略します*1

認証の設定

Swagger の設定は、startup.cs を開き、ConfigureServices メソッドの中で AddSwaggerGen の中身を以下のように変えます。

services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "SwaggerSandbox.AspnetCore50WithAzureAdBearerSet", Version = "v1" });

    c.AddSecurityDefinition("OAuth2", new OpenApiSecurityScheme
    {
        Name = "Authorization",
        Type = SecuritySchemeType.Http,
        Scheme = "Bearer",
        BearerFormat = "JWT",
        In = ParameterLocation.Header,
        Description = "IdP から取得したトークンをセットします(先頭の 'Bearer' + space は不要)。",
    });

    //これをつけることですべての API に認証を適用する
    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "OAuth2" }
            },
            // Scope は必要に応じて入力する
            new string[] {}
        }
    });
});

スーパーざっくりな解説だと、

  • AddSecurityDefinition で "OAuth2" って name とその設定を定義しています。
  • AddSecurityRequirement ですべての API に対して前述で定義した "OAuth2" を適用しています。

ここではなんとなくで "oauth2" って name にしましたが、この name が Swagger UI で表示されるため、例えば "Azure AD B2C JWT" とか具体的にした方がよさげです。
より詳細を知りたかったりカスタマイズしたい場合は公式ドキュメントを参考にいじっていくことができます。

デバッグ実行

デバッグ実行して、Authorize のボタンをクリックします。

f:id:beachside:20210120173841p:plain

トークンを入力する画面が出てきます。認証プロバイダーから取得したトークンをセットします。Bearer だと Authorization ヘッダーの先頭に "Bearer" + " " + トークンってかたちになりますが、ここでは認証プロバイダーから取得したトークンのみをセットしてあげればよりです。"Bearer" + " " は API のコール時に Swagger UI 側でつけてくれます。

Authorize ボタンをクリックします。クリックした後は、このウインドウを閉じます。

f:id:beachside:20210122003047p:plain

南京錠にロックかかった感じになっていれば OK です。この段階で API にロックがかかってなかったり南京錠のアイコン自体が表示されてなければ AddSecurityRequirement の設定が抜けてるとかおかしいとかです。

f:id:beachside:20210120174514p:plain

API を実行すると、Authorization ヘッダーを正しくセットして送信していることがわかります。

f:id:beachside:20210120174754p:plain

完成版のサンプルコード

ということで Bearer トークンをセットするやーつのコードは GitHub のこちらにおいてあります。

https://github.com/beachside-project/aspnet-core-openapi-sandbox/tree/main/src/AspnetCore50WithAzureAdBearerSet

おわりに (次回予告: Swagger UI で Azure AD 認証)

今回は Authorize ボタンをクリックしたらトークンをセットできるだけの簡易なことをやりました。

次回は、ASP.NET Core + Azure AD の Web API で、Swagger UI の Authorize ボタンから AAD に実際に認証にいってトークンをセットしてくれるところをやります(追記: やりました)。

blog.beachside.dev

参考

Swashbuckle 関連

Swashbuckle.AspNetCore

ASP.NET Core 関連

ASP.NET Core で複数の環境を使用する | Microsoft Docs

XML ドキュメントのコメントを挿入する - Visual Studio | Microsoft Docs

XML コメントによるコードの文書化 | Microsoft Docs

*1:そいえば Azure AD と ASP.NET Core のワークショップののドキュメント作ったりしてます: https://github.com/beachside-project/azure-ad-workshop