はじめに
Swashbuckle はまだプレリリースの段階ではあるけど、
ASP.NET Core にも対応しているみたい。
github.com
Swashbuckle を使えば、Web API の実装から
Swagger Definitions を生成できる。
温かみのある手作業で、
Web API の Swagger Definitions を書いていたけど、
もう限界なので試してみることにした。
プロジェクト新規作成
今回は Web API だけ使うので、
ASP.NET Core の Web API プロジェクトを作成しておく。
Swashbuckle をインストール
project.json
の dependencies
に
"Swashbuckle": "6.0.0-beta902"
を追加して、パッケージをリストア。
Swagger を使うように Startup を修正
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace SwaggerSample
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSwaggerGen();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUi();
}
}
}
モデルを定義
Web API で返すモデルだけでなく、
入力用のモデルと、
エラー用のモデルも定義してみた。
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace SwaggerSample.Models
{
public class Item
{
public string Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
public class ItemInputModel
{
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set; }
}
public class ErrorModel
{
public Dictionary<string, string> Errors { get; } = new Dictionary<string, string>();
}
}
コントローラーを作成
プロジェクトを新規作成すると ValuesController ってのが作られるけど、
そいつは削除してしまって、
ItemsController を作成。
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using SwaggerSample.Models;
namespace SwaggerSample.Controllers
{
[Route("api/[controller]")]
public class ItemsController : Controller
{
private static readonly ConcurrentDictionary<string, Item> _items
= new ConcurrentDictionary<string, Item>();
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<Item>), 200)]
public IActionResult Get()
{
return Ok(_items.Values);
}
[HttpGet("{id}")]
[ProducesResponseType(typeof(Item), 200)]
[ProducesResponseType(typeof(ErrorModel), 404)]
public IActionResult Get(string id)
{
Item item;
if (_items.TryGetValue(id, out item) == false)
{
return NotFound(new ErrorModel
{
Errors =
{
[ "id"]= $"ID = {id} のアイテムは存在しません。"
}
});
}
else
{
return Ok(item);
}
}
[HttpPost]
[ProducesResponseType(typeof(Item), 200)]
[ProducesResponseType(typeof(ErrorModel), 400)]
public IActionResult Post([FromBody]ItemInputModel inputModel)
{
if (ModelState.IsValid == false)
{
return BadRequest(CreateValidationError());
}
var item = new Item
{
Id = Guid.NewGuid().ToString(),
Title = inputModel.Title,
Description = inputModel.Description,
};
_items.TryAdd(item.Id, item);
return Ok(item);
}
[HttpPut("{id}")]
[ProducesResponseType(typeof(Item), 200)]
[ProducesResponseType(typeof(ErrorModel), 400)]
[ProducesResponseType(typeof(ErrorModel), 404)]
public IActionResult Put(string id, [FromBody]ItemInputModel inputModel)
{
Item item;
if (_items.TryGetValue(id, out item) == false)
{
return NotFound(new ErrorModel
{
Errors =
{
["id"] = $"ID = {id} のアイテムは存在しません。"
}
});
}
if (ModelState.IsValid == false)
{
return BadRequest(CreateValidationError());
}
item.Title = inputModel.Title;
item.Description = inputModel.Description;
_items[id] = item;
return Ok(item);
}
[HttpDelete("{id}")]
[ProducesResponseType(typeof(Item), 200)]
[ProducesResponseType(typeof(ErrorModel), 404)]
public IActionResult Delete(string id)
{
Item item;
if (_items.TryRemove(id, out item))
{
return Ok(item);
}
else
{
return NotFound(new ErrorModel
{
Errors =
{
["id"] = $"ID = {id} のアイテムは存在しません。"
}
});
}
}
private ErrorModel CreateValidationError()
{
var error = new ErrorModel();
foreach (var pair in ModelState)
{
error.Errors.Add(
pair.Key,
string.Join(
Environment.NewLine,
pair.Value.Errors.Select(e => e.ErrorMessage)
)
);
}
return error;
}
}
}
戻り値の型を IActionResult にする場合、
ProducesResponseTypeAttribute を使って、
Swashbuckle にモデルの型を教えてあげないといけない。
NotFound とか BadRequest とか扱いたいなら必須。
Swagger UI を表示
Visual Studio から実行したいところだけど、マシンパワーが貧弱で起動が遅いので、
> dotnet run
でサービスを実行。
ブラウザで localhost:5000/swagger/ui
にアクセスしてみる。
Swagger UI が表示された。
ここから Web API を実際に呼び出すことができて便利。
Swagger Definitions を表示
Swagger UI に Swagger Definitions の URL が表示されているので、
ブラウザでアクセスしてみる。
Swagger Definitions の JSON が表示された。
モデルの情報もちゃんと含まれている。
おわりに
Swagger UI は Web API のデバッグに超便利だった。
Swagger Definitions も生成してくれるので、
Swagger Codegen に渡せばクライアントのソースコードまで生成できる。
Swagger Codegen のインストールが面倒なら、
公式がホストしている Swagger Editor に貼り付けて生成する手もある。
Swagger Definitions を YAML でゴリゴリ書いていたけど、
Web API からの自動生成を経験してしまったので、
もう戻れないな。