ASP.NET Core 2.0 でも Swashbuckle.AspNetCore は使えた

Swashbuckle.AspNetCore は ASP.NET Core 2.0 に対応していないから使えないと思っていたが、 勘違いだった。

github.com

Swashbuckle.AspNetCore のイシューを読んで発覚。

github.com

実際に試してみたところ、ASP.NET Core 2.0 プロジェクトに Swashbuckle.AspNetCore は追加できたし、ASP.NET Core 1.1.x のパッケージが芋づる式に追加されることもなかった。

今回作成した超単純なサンプルは次の通り。

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.Swagger;
using System.Collections.Generic;

namespace SampleApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            // Swagger を使えるようにする
            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new Info
                {
                    Title = "Sample API",
                    Version = "v1"
                });
            });
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();

            // Swagger UI を使う
            app.UseSwagger();
            app.UseSwaggerUI(options =>
            {
                options.SwaggerEndpoint("/swagger/v1/swagger.json", "Sample API V1");
            });
        }
    }

    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        [HttpGet("{id}")]
        public string Get(int id)
        {
            return "value";
        }

        [HttpPost]
        public void Post([FromBody]string value)
        {
        }

        [HttpPut("{id}")]
        public void Put(int id, [FromBody]string value)
        {
        }

        [HttpDelete("{id}")]
        public void Delete(int id)
        {
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
}

Swagger UI はちゃんと表示できた。

f:id:griefworker:20170823170224p:plain

この前試したときは、ASP.NET Core 1.1.2 関連のパッケージが追加されそうで慌ててキャンセルしたんだけど、見間違いだったんだろうか…。 まぁ、Swashbuckle.AspNetCore が使えることが分かったんで、ASP.NET Core 2.0 にアップグレードするにあたっての障壁は無くなったな。

スパイス

夏は不思議とカレーが食べたくなるもので、中洲川端にある『スパイス』に行ってきた。店には所々にガンダムグッズが置いてあって、奇妙な感じだが嫌いじゃない。

注文したのはカツカレー(980円)。 カレーはマイルドで、甘さと辛さのバランスがちょうどいい。 トンカツはサックサクに揚がっており、カレーとの相性も良く旨かった。

スパイスという店名なので、インドカレースリランカカレーのような、かなりスパイシーなカレーを出す店かと思っていたが、どちらかというと欧風カレーに近いカレーだった。

関連ランキング:喫茶店 | 中洲川端駅呉服町駅祇園駅

佐世保観光

今年こそは夏期休暇らしいことをしたいと思い、佐世保にある『九十九島パールシーリゾート』に行ってきた。道中、尋常じゃない雨が降ってどうなるかとヒヤヒヤしたが、佐世保は晴れていて安心した。

お目当は水族館『海きらら』。 コンビニで前売り券を買っておいたので、100円安く入れた。

大水槽。

イルカショーは観客が多くて立ち見だった。30分以上前から場所取りしていたみたい。この暑い中、自分には無理だ。ジャンプは何とか見れたので良しとしよう。

客席の方は水しぶきをかなり浴びたみたいだ。

海きららはクラゲにも力を入れてるみたい。

海きららを見終わって、遅めの昼食を食べたら、もう16時過ぎ。福岡に帰るために佐世保駅へ向かった。

電車は高いので、高速バスを予約。バスの時間まで1時間以上あるので、佐世保駅の周辺を散策することにした。

佐世保駅のすぐ近くに立派な『カトリック三浦町教会』が。高台にあって雰囲気ある。

佐世保駅のすぐ裏には『させぼ五番街』があった。入ってるテナントは福岡のキャナルシティや木の葉モールと似た傾向。

すぐ側が海でムードがあった。

港も。

佐世保バーガーの有名店『ヒカリ』が出店していたのに、運悪く腹減って無くて食べなかったのが心残り。あと四ヶ町アーケードにも行けなかった。次の機会があれば行きたい。

中華そば かなで

最近は豚骨よりも醤油を好む傾向になってきた。こってりが辛くなったのは、もう若くないということだろうか。今回行ったのは、東比恵にある『中華そば かなで』。

中華そばを注文。

多加水麺はプチっとした食感が新鮮。 チャーシューは低温調理してあって、 チャーシューとは思えないジューシーさだった。 清湯鶏がらスープに古式しょうゆを合わせたスープは、あっさりでいて、味が奥深い。 中華そばとして完成されている印象。 福岡でこんな醤油が食べられるとはね。

中華そば かなで

食べログ 中華そば かなで

Microsoft.AspNetCore.Mvc.ApiExplorer を使って Web API ドキュメントを自作

開発している Web APIASP.NET Core 2.0 に移行しようと思っていたが、Web API のドキュメント生成に使っている Swashbuckle.AspNetCore がまだ 2.0 に対していないっぽくて一時中断。

ASP.NET Core MVC にはもとから Microsoft.AspNetCore.Mvc.ApiExplorer というのが提供されていて、これを使うことでちょっとした Web API のドキュメントを出力できる。ただ、SwaggerUI ほど高機能では無いし、Web API ドキュメントを表示するためのコントローラーとビューを自分で書く必要があるみたいだ。手軽ではない。

試してみたコードがこちら。

using System.Collections.Generic;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace SampleApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            // AddMvc 内で ApiExplorer も登録されている
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvcWithDefaultRoute();
        }
    }

    [Route("api/[controller]")]
    [ApiExplorerSettings(GroupName = "Values API", IgnoreApi = false)]
    public class ValuesController : Controller
    {
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        [HttpGet("{id}")]
        public string Get(int id)
        {
            return "value";
        }

        [HttpPost]
        public void Post([FromBody]string value)
        {
        }

        [HttpPut("{id}")]
        public void Put(int id, [FromBody]string value)
        {
        }

        [HttpDelete("{id}")]
        public void Delete(int id)
        {
        }
    }

    public class HomeController : Controller
    {
        readonly IApiDescriptionGroupCollectionProvider _apiExplorer;

        public HomeController(IApiDescriptionGroupCollectionProvider apiExplorer)
        {
            _apiExplorer = apiExplorer;
        }

        // API ドキュメントを表示する
        public IActionResult Index()
        {
            return View(_apiExplorer);
        }
    }
}

Web API のドキュメントを表示する Home/Index ビュー。

@using Microsoft.AspNetCore.Mvc.ApiExplorer
@model IApiDescriptionGroupCollectionProvider

<html>
<head>
    <meta charset="utf-8" />
    <title>Sample API</title>
</head>
<body>
    <h1>Sample API ドキュメント</h1>

    @foreach (var group in Model.ApiDescriptionGroups.Items)
    {
        <h2>@group.GroupName</h2>

        @foreach (var api in group.Items)
        {
            <h3>@api.HttpMethod @api.RelativePath</h3>

            <div class="parameters">
                <h4>パラメーター</h4>
                @if (0 < api.ParameterDescriptions.Count)
                    {
                    <table>
                        <thead>
                            <tr>
                                <th>名前</th>
                                <th></th>
                                <th>Constrains</th>
                                <th>デフォルト値</th>
                                <th>必須</th>
                            </tr>
                        </thead>
                        <tbody>
                            @foreach (var parameter in api.ParameterDescriptions)
                            {
                                <tr>
                                    <td>@parameter.Name,  (@parameter.Source.Id)</td>
                                    <td>@parameter.Type?.FullName</td>

                                    @if (parameter.RouteInfo != null)
                                    {
                                        <td>@string.Join(",", parameter.RouteInfo.Constraints?.Select(c => c.GetType().Name).ToArray())</td>
                                        <td>@parameter.RouteInfo.DefaultValue</td>
                                        <td>
                                            @if (parameter.RouteInfo.IsOptional == true)
                                            {
                                                <text></text>
                                            }
                                        </td>
                                    }
                                    else
                                    {
                                        <td></td>
                                        <td></td>
                                        <td></td>
                                    }
                                </tr>
                            }
                        </tbody>
                    </table>
                }
                else
                {
                    <i>なし</i>
                }
            </div>

            <div class="responses">
                <h4>レスポンス</h4>

                <table>
                    <thead>
                        <tr>
                            <th>ステータスコード</th>
                            <th></th>
                            <th>メディアタイプ</th>
                        </tr>
                    </thead>
                    <tbody>
                        @foreach (var response in api.SupportedResponseTypes)
                        {
                            <tr>
                                <td>@response.StatusCode</td>
                                <td>@response.Type?.FullName</td>
                                <td>
                                    <ul>
                                        @foreach (var responseFormat in response.ApiResponseFormats)
                                        {
                                            <li>@responseFormat.MediaType</li>
                                        }
                                    </ul>
                                </td>
                            </tr>
                        }
                    </tbody>
                </table>
            </div>
        }
    }
</body>
</html>

実行すると、こんなドキュメントが表示される。

f:id:griefworker:20170817141951p:plain

取得した Web API の情報を自分で好きなように出力するので、自由度は高い。でもやっぱり Swagger UI の方がいいな。

2017 年 8 月 23 日訂正

ASP.NET Core 2.0 で Swashbuckle.AspNetCore 使えました。

『服を着るならこんなふうに(5)』を読んだ

5巻では、体型を隠すコーディネートと、結婚式の二次会なんかで使えるスマートカジュアルについて解説してあったが、自分には使えそうにない。

体型は今のところキープできているし、 周りはほとんど結婚してしまったので、 結婚式の二次会に行くことも無さそう。

セットアップはコーディネートの幅が広がるので、持っておくと便利そうではある。安い買い物ではないので、すぐにとはいかないが。

『王様達のヴァイキング(1)〜(13)』を読んだ

Podcast で IT エンジニアにお薦めと聴いて、Kindle で一気に読んだ。

天才的なハッカーでクラッカーでもある主人公の是枝が、もう1人の主人公であるエンジェル投資家の坂井と組んで、 世界征服を目指す。実際は、是枝のクラッカーとしての能力を生かして、サイバー犯罪と戦うマンガ。

出てくるサイバー犯罪は、どれも現実に起きておかしくないもので、これはないなと思うことがなかった。Web サービスを開発運用する身として、読んでて身が引き締まった。IT エンジニアにお薦めというのも納得。

是枝の宿敵と言えるキャラも出てきて、幾度となく熱いサイバーバトルを繰り広げるが、13 巻ともなると最終決戦かというくらいの盛り上がり。まさかこんな展開になるとは。熱い。熱すぎる。続きが気になって仕方ない。

あと、坂井がいいね。第一印象はめっちゃ胡散臭かったけど、すぐにお気に入りになった。脳みそが若かったりアンテナが高いところはもちろん良いが、それよりも、自分を馬鹿にしている後輩に対して、そういう気概があるべきだと言えるところが格好良い。 こんなアラフォーになりたいと思った。