ASP.NET Core Web API で CRUD & Swagger/OpenAPI でテスト

# はじめに
[前回](https://qiita.com/tamtamyarn/items/5a755d4d5d59592a82cd) ASP.NET Core Web API で Entity Framework Core を使って SQL Server から単一テーブルのデータを取得する API を作ったので、今回は他の CRUD も作っていきたいと思います。動作確認しやすいように、Swagger/OpenAPI も導入します。

# Swagger/OpenAPI 導入

では Swagger/OpenAPI を導入していきましょう。[MS のドキュメント](https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/web-api-help-pages-using-swagger?view=aspnetcore-2.2)では２種類のライブラリが紹介されてますが、今回は開発が活発そうな [NSwag](https://github.com/RicoSuter/NSwag) の方を使います。

パッケージマネージャーコンソールから以下のコマンドを実行。

```
> Install-Package NSwag.AspNetCore
```

`Startup.cs` で NSwag を有効化。(OpenAPI 3.0)

```csharp:Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SimpleWebApi.Data;
using NSwag.AspNetCore; //追加

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

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<BookContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            services.AddOpenApiDocument(); //追加
        }

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

            app.UseOpenApi(); //追加
            app.UseSwaggerUi3(); //追加

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}
```

/swagger にアクセスして API の一覧（まだ１個しかないけど）が表示されることを確認。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/383318/0685bffb-1e58-3e10-c414-a13808d78da8.png)

これで UI から簡単に API のテストができるようになったので、CRUD API を順番に作っていきます。

# GET（単一リソース）

前回はテーブルのレコードすべてを返す API を作ったので、今回は特定のレコードのみ返す API を作ります。

```csharp:Controllers/BooksController.cs
using System.Collections.Generic;
using System.Linq; //追加
using Microsoft.AspNetCore.Mvc;
using SimpleWebApi.Data;
using SimpleWebApi.Entities;

namespace SimpleWebApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BooksController : ControllerBase
    {
        //省略//
     
        //追加//
        [HttpGet("{id}")]
        public ActionResult<Book> Get(int id)
        {
            var book = context.Books.FirstOrDefault(b => b.BookId.Equals(id));
            return book;
        }
    }
}
```

Swagger UI でテスト実行。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/383318/59c89c78-bd97-d523-eaa7-813b22b64edd.png)

# Create（新規作成）

新しいレコードを追加するPOST メソッドを追加します。

```csharp:Controllers/BooksController.cs
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using SimpleWebApi.Data;
using SimpleWebApi.Entities;

namespace SimpleWebApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BooksController : ControllerBase
    {
        //省略//
        
        //追加//
        [HttpPost]
        public ActionResult<Book> Create(Book book)
        {
            context.Books.Add(book);
            context.SaveChanges();
            return CreatedAtAction(nameof(Get), new { id = book.BookId }, book);
        }
    }
}
```

Swagger UI でテスト実行。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/383318/c145f6a5-e0a7-6c85-e577-ffad67f6949d.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/383318/3b4fb572-ac0e-6bc7-89e9-d742890ead31.png)

bookId は 0 にするか、リクエストボディに含めずに実行すればちゃんと連番で ID を生成してくれます。

# Update（更新）

レコードを更新する PUT メソッドを追加します。

```csharp:Controllers/BooksController.cs
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; //追加
using SimpleWebApi.Data;
using SimpleWebApi.Entities;

namespace SimpleWebApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BooksController : ControllerBase
    {
        //省略//

        //追加//
        [HttpPut]
        public IActionResult Update(Book book)
        {
            context.Entry(book).State = EntityState.Modified;
            context.SaveChanges();
            return NoContent();
        }
    }
}
```

Swagger UI でテスト実行。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/383318/c7ecb7c2-a345-5c66-161a-b57a88db0475.png)

Get でちゃんと更新されていることを確認。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/383318/b0645ecd-7021-e4e2-451e-2e697ae84db0.png)

# DELETE（削除）

レコードを削除する DELETE メソッドを追加します。

```csharp:Controllers/BooksController.cs
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SimpleWebApi.Data;
using SimpleWebApi.Entities;

namespace SimpleWebApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BooksController : ControllerBase
    {
        //省略//

        //追加//
        [HttpDelete("{id}")]
        public IActionResult Delete(int id)
        {
            var book = context.Books.FirstOrDefault(b => b.BookId.Equals(id));

            context.Books.Remove(book);
            context.SaveChanges();

            return NoContent();
        }
    }
}
```

Swagger UI でテスト実行。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/383318/74abd891-2b4f-2797-8f74-dd3d4032d73d.png)

Get でちゃんと削除されていることを確認。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/383318/23ce60e7-2211-8562-b708-0817b3239b28.png)

# ソースコード
https://github.com/tamtamyarn/SimpleWebApi2