One Step Ahead

プログラミングやエンジニアリング全般について書いていきます

Swashbuckleを使用して、凝らないSwaggerドキュメントを生成する。(ASP.NET WebAPI偏)

はじめに


ASP.NET WebAPI(.NET Framework)とSwashbuckleをして、コードファーストアプローチからSwaggerドキュメントを生成します。
『何故に今更、ASP.NET WebAPIを使うの?ASP.NET Coreじゃダメなの?』と思った方もいらっしゃるかと思いますが、業務では.NET Frameworkを使う機会がいまだに多いという個人的な事情です...
個人開発であれば、ASP.NET Core一択です。

まずはコードを見せろ。という方はこちらをどうぞ

開発環境


  • Visual Studio 2019
  • .NET Framework 4.7.2
  • C# 7
  • ASP.NET WebAPI
  • Swashbuckle5.6.0

Swashbuckleのインストール


Swashbuckleは.NET用に提供されているSwaggerコードファーストアプローチ用のライブラリです。 今回は、ASP.NET WebAPIで作成していますが、もちろんASP.NET Coreにも対応しています。

PackageManagerから『Swashbuckle』をインストールしてください。

Install-Package Swashbuckle

f:id:EaE:20210223150126p:plain 

凝らないドキュメントの生成


今回は、コードファースト(ボトムアップ型)のアプローチでSwaggerドキュメントを生成していくので、APIのコードが必要となりますが、そこについてはプロジェクトによって十人十色なため割愛します。以降に紹介するコードは、サンプルとして公開しているので、そちらを参考にしてください。 

Swashbuckleをプロジェクトに追加すると、App_Startディレクトリの中にSwaggerConfigが作成されます。下記のようなファイルが追加されているかと思います。(実際に生成されるファイルには大量にコメントがされているはずなので、簡略化しています。)

[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]

namespace WebAPIExample
{
    /// <summary>
    /// Swagger Configuration.
    /// </summary>
    public class SwaggerConfig
    {
        /// <summary>
        /// Register swagger configuration.
        /// </summary>
        public static void Register()
        {
            var thisAssembly = typeof(SwaggerConfig).Assembly;

            GlobalConfiguration.Configuration
                .EnableSwagger(c =>
                    {})
                .EnableSwaggerUi(c =>
                    {});
        }
    }
}

ここから特別な設定などは追加せず、凝らないドキュメントの生成とUIの設定を行っていきます。

【ドキュメントの生成】

まずはドキュメントの生成を行っていきます。
ドキュメント生成に関わっているのは, EnableSwagger(c => {})の部分で、この中に設定を追加していきます。実際のAPIに関する基本情報を追記したのが下記になります。

[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]

namespace WebAPIExample
{
    /// <summary>
    /// Swagger Configuration.
    /// </summary>
    public class SwaggerConfig
    {
        /// <summary>
        /// Register swagger configuration.
        /// </summary>
        public static void Register()
        {
            var thisAssembly = typeof(SwaggerConfig).Assembly;

            GlobalConfiguration.Configuration
                .EnableSwagger("docs/{apiVersion}/swagger", c =>
                    {
                        // api infomation.
                        c.SingleApiVersion("v1", "WebAPIExample")
                            .Description("This repository is example of ASP.NET WebAPI (.NET Framework) with Swashbuckle.")
                            .License(l =>
                                {
                                    l.Name("MIT")
                                     .Url("https://github.com/atEaE-samples/aspnet-webapi-swashbuckle-example/blob/master/LICENSE");
                                })
                            .TermsOfService("There are no restrictions on use.")
                            .Contact(cb =>
                                {
                                    cb.Name("atEaE")
                                      .Email("sample@example.com")
                                      .Url("https://github.com/atEaE");
                                });

                        // use schemes
                        c.Schemes(new[] { "http", "https" });
                    })
                .EnableSwaggerUi(c =>
                    {});
        }
    }
}

EnableSwagger()の第一引数に指定している"docs/{apiVersion}/swagger"文字列は、Swaggerドキュメントのエンドポイントを表しています。{apiVersion}の部分は、後述するAPIVersionが自動的にパスに反映されます。

今回のAPIはシングルAPIなので、c.SingleApiVersion()を使用して、ドキュメントを生成しています。 第一引数に"v1"APIのVersionを、第二引数にWebAPIExampleAPIのタイトルを設定します。
SingleApiVersion()メソッドは、Swaggerドキュメントのinfoに相当するInfoBuilderを返却するため、メソッドチェーンさせることで設定情報を記述することができます。
InfoBuilderで設定可能な項目は下記の通りです。

  • Description() : ドキュメントについての説明
  • License() : APIのライセンス
  • TermsOfService() : 利用規約
  • Contact() : APIについての問い合わせ先

Contact(), License()はそれぞれ、Actionを引数として受け取るため、さらに詳細なプロパティを設定することができます。

c.Schemes()には、APIの通信プロトコルを設定しています。この例では、https, httpの2つを設定しています。

【SwaggerUIの有効化】

何となく気づいている方もいると思いますが、SwaggerUIの有効化はすでに完了しています。
EnableSwaggerUi(c =>{});が設定されているため、SwaggerUIが有効となっています。

ここまでで、ドキュメント生成に関する準備は終了です。

Swaggerドキュメントの確認


最後にSwaggerドキュメントを確認していきます。 サンプルAPIをDebug起動させると、下記のようなIndexページに遷移するかと思います。

f:id:EaE:20210223181245p:plain

Indexページに遷移したら、https://localhost:44377/swagger もしくは https://localhost:44377/swagger/ui/indexに遷移することで、SwaggerUIのインデックスページを確認することができます。

f:id:EaE:20210223181435p:plain

f:id:EaE:20210223181514p:plain

メソッドに応じたパス情報、返却されるモデル情報についても確認することができます。 UIの元になっているSwaggerドキュメントは、先ほど設定したパスにアクセスすることで確認できます。
(パステンプレートに、"docs/{apiVersion}/swagger"を。Versionにv1を設定しているので、https://localhost:44377/docs/v1/swaggerにアクセスすることで、ドキュメントを確認することが出来ます。 )

{
    "swagger": "2.0",
    "info": {
        "version": "v1",
        "title": "WebAPIExample",
        "description": "This repository is example of ASP.NET WebAPI (.NET Framework) with Swashbuckle.",
        "termsOfService": "There are no restrictions on use.",
        "contact": {
            "name": "atEaE",
            "url": "https://github.com/atEaE",
            "email": "sample@example.com"
        },
        "license": {
            "name": "MIT",
            "url": "https://github.com/atEaE-samples/aspnet-webapi-swashbuckle-example/blob/master/LICENSE"
        }
    },
    "host": "localhost:44377",
    "schemes": [
        "http",
        "https"
    ],
    "paths": {
        "/api/Users": {
            "get": {
                "tags": [
                    "Users"
                ],
                "operationId": "Users_Get",
                "consumes": [],
                "produces": [
                    "application/json",
                    "text/json",
                    "application/xml",
                    "text/xml"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/UserResponse"
                        }
                    }
                }
            },
            "post": {
                "tags": [
                    "Users"
                ],
                "operationId": "Users_Post",
                "consumes": [
                    "application/json",
                    "text/json",
                    "application/xml",
                    "text/xml",
                    "application/x-www-form-urlencoded"
                ],
                "produces": [],
                "parameters": [
                    {
                        "name": "value",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/UserRequest"
                        }
                    }
                ],
                "responses": {
                    "204": {
                        "description": "No Content"
                    }
                }
            }
        },
        "/api/Users/{id}": {
            "get": {
                "tags": [
                    "Users"
                ],
                "operationId": "Users_Get",
                "consumes": [],
                "produces": [
                    "application/json",
                    "text/json",
                    "application/xml",
                    "text/xml"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "type": "integer",
                        "format": "int32"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/UserResponse"
                        }
                    }
                }
            },
            "put": {
                "tags": [
                    "Users"
                ],
                "operationId": "Users_Put",
                "consumes": [
                    "application/json",
                    "text/json",
                    "application/xml",
                    "text/xml",
                    "application/x-www-form-urlencoded"
                ],
                "produces": [],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "type": "integer",
                        "format": "int32"
                    },
                    {
                        "name": "value",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/UserRequest"
                        }
                    }
                ],
                "responses": {
                    "204": {
                        "description": "No Content"
                    }
                }
            },
            "delete": {
                "tags": [
                    "Users"
                ],
                "operationId": "Users_Delete",
                "consumes": [],
                "produces": [],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "type": "integer",
                        "format": "int32"
                    }
                ],
                "responses": {
                    "204": {
                        "description": "No Content"
                    }
                }
            }
        }
    },
    "definitions": {
        "UserResponse": {
            "type": "object",
            "properties": {
                "users": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/User"
                    }
                }
            }
        },
        "User": {
            "type": "object",
            "properties": {
                "familyName": {
                    "type": "string"
                },
                "firstName": {
                    "type": "string"
                },
                "userName": {
                    "type": "string"
                },
                "email": {
                    "type": "string"
                },
                "gender": {
                    "type": "string"
                },
                "age": {
                    "format": "int32",
                    "type": "integer"
                },
                "bio": {
                    "type": "string"
                }
            }
        },
        "UserRequest": {
            "type": "object",
            "properties": {}
        }
    }
}

さいごに


  • シングルAPIでコードファーストアプローチ型のドキュメント生成をするのは簡単
  • 凝ったことをしないのであれば、API情報の設定のみで残りは設定いらず。

参考・引用