ASP.NET Core Web APIのチュートリアルを丁寧にやってみた

C#

ASP.NET Core Web APIの公式チュートリアルの内容をより丁寧に解説してみました。

ToDoアイテムを管理する簡単なAPIを作成していきます。

※公式チュートリアルはこちら

チュートリアル: ASP.NET Core で Web API を作成する
ASP.NET Core で Web API をビルドする方法を学習します。

本記事の対象

  • C#、Web APIの基本的な知識がある人

環境

  • Visual Studio 2022
  • .NET 6

プロジェクトの作成と実行

Visual Studioを起動したら、新規プロジェクトの作成画面を開き、「ASP.NET Core Web API」を選択します。

プロジェクト名はチュートリアルと同じ「TodoApi」にしました。

ターゲットフレームワークには「.NET 6.0(長期的なサポート)」を選択します。

プロジェクトの作成が完了したら、「Ctrl+F5」でデバッグなしで実行してみましょう。

次の画面が表示された場合は「はい」をクリックし、その後のセキュリティ警告画面でも「はい」をクリックして証明書をインストールします。

ブラウザが起動し、以下のようなSwagger UIのページが表示されます。

Swagger UIとは

SwaggerとはAPIの仕様を管理するフレームワークで、Swagger UIはそれを構成するツールの1つです。 Swagger UIを使うと、設定されたAPIのドキュメントをWebベースで見やすく表示してくれます。ページ上からリクエストを送信して結果を確認することもできます。

ASP.NET Coreでは「Swashbuckle」というパッケージをインストールすることで使用することができ、.NET 5以降では標準でインストールされています。(プロジェクト作成時に「Open APIサポートを有効にする」にチェックを入れた場合)


表示されている「WeatherForecast」とはプロジェクトに最初から用意されているサンプルのAPIです。試しに挙動を確認してみましょう。

「GET /WeatherForecast」をクリックすると詳細が開くので、「Try it out」→「Execute」の順でクリックして、リクエストを投げてみます。

すると、以下のようなレスポンスが返ってきました。
WeatherForecastは日付ごとの天気予報を返すAPIなので、Response Bodyには日時や気温が表示されています。

以上でAPIの動作確認ができました。

次からToDoアプリの作成していきますが、その前に今後起動する際のURLをSwagger UIからAPIに変えておきましょう。

Properties/launchSettings.json内のlaunchUrlを以下のように変更すればOKです。
launchSettings.jsonとは、ローカルでのアプリケーション起動に関する設定内容が記述されたファイルです。

...
  "profiles": {
    "TodoApi": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "launchUrl": "api/todoitems",
      "applicationUrl": "https://localhost:<ポート番号>;http://localhost:<ポート番号>",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
...

これで今後はSwagger UIではなく「https://localhost:<ポート番号>/api/todoitems」が開くようになります。

モデルクラスとDBContextの追加

ToDoアイテムを管理するために、まずはモデルクラスを作成します。

「TodoApi」プロジェクトを右クリックし、「追加」→「新しいフォルダー」で「Models」フォルダを作成してください。さらに「Models」フォルダを右クリックし、「追加」→「クラス」で「TodoItem」というクラスを作成します。

作成できたら、TodoItemクラスのコードを以下の通り書き換えましょう。

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string? Name { get; set; }
        public bool IsComplete { get; set; }
    }
}

Idプロパティにはテーブル内の主キーが格納されます。

次に、モデルクラスとDBを対応付けるためにDbContextクラスを作成しますが、その前にEntityFramework Coreをインストールしておきましょう。

現段階ではDBを用意せずにメモリ上のDBだけで操作するため、インメモリ用のEFCoreをインストールします。

Visual Studio上部の「ツール」→「NuGet パッケージ マネージャー」→「ソリューションの NuGet パッケージの管理」をクリックし、「参照」タブから「Microsoft.EntityFrameworkCore.InMemory」と検索します。

TodoApiプロジェクトにチェックを入れたら、バージョンが最新であることを確認し、インストールをクリックしてください。

同様の手順で「Microsoft.EntityFrameworkCore.Design」もインストールしておきましょう。

インストールが完了したら、「Models」フォルダを右クリックし、「追加」→「クラス」で「TodoContext」というクラスを作成します。

using Microsoft.EntityFrameworkCore;
using System.Diagnostics.CodeAnalysis;

namespace TodoApi.Models
{
    public class TodoContext : DbContext
    {
        public TodoContext(DbContextOptions<TodoContext> options)
            : base(options)
        {
        }

        public DbSet<TodoItem> TodoItems { get; set; } = null!;
    }
}

次に、作成したTodoContextをアプリに登録するために、Program.csを変更します。

using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));
//builder.Services.AddSwaggerGen(c =>
//{
//    c.SwaggerDoc("v1", new() { Title = "TodoApi", Version = "v1" });
//});

var app = builder.Build();

if (builder.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    //app.UseSwagger();
    //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TodoApi v1"));
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Swaggerの呼び出している記述をコメントアウトしています。
また、「AddDbContext」でDBContextクラスを登録し、 「UseInMemoryDatabase」でDBContextクラスが「TodoList」という名前のメモリ内DBを使用することを指定しています。

スキャフォールディング

スキャフォールディングを実行してCRUDの各メソッドを自動で生成してみましょう。

「Controllers」フォルダを右クリックして、「追加」→「新規スキャフォールディングアイテム」をクリックします。

「Entity Framework を使用したアクションがある API コントローラー」を選択してから、 [追加] をクリック。

モデルクラスに先ほど作成した「TodoItem」クラスを、データコンテキストクラスに「TodoContext」を選択して「追加」をクリックすると、スキャフォールディングが開始されます。

スキャフォールディングが完了すると、「TodoItemsController」が自動的に作成されます。

TodoItemsControllerの確認

作成されたコントローラーの頭の部分を抜粋して確認してみましょう。
わかりやすくするために一部コメントを追加しています。

...   
   [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;

        // TodoContextクラスをControllerに挿入
        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }
...

ここで使用されている属性やクラスについて簡単に説明します。

  • [Route属性]
    この属性を付与することで「属性ルーティング」(Program.csではなくController側のRoute属性によりルートの指定を行う)が使用できます。
    [Route(“api/[controller]”)]の場合は「/api/コントローラー名」というURLが生成されます。コントローラー名には○○Controllerの○○の部分が入ります。
  • [ApiController]属性
    この属性を付与することでコントローラーがWeb APIのリクエストに応答することを示します。これにより上述の「属性ルーティング」や、「自動的なHTTP応答」(モデル検証でエラーが発生したら自動で400エラーがレスポンスとして返される)などの様々な機能が有効化されます。
    詳細は以下のドキュメントを参照してください。
    ASP.NET Core を使って Web API を作成する
  • ControllerBaseの継承
    ビューを使用するMVCアプリの場合はControllerクラスを継承しますが、ビューが存在しないWeb APIではControllerBaseを継承します。ControllerBaseクラスにはHTTPリクエストの処理に役立つメソッドなどが多く含まれています。
  • Contextクラスの挿入
    作成したTodoContextクラスは、DIを使用してControllerに挿入されています。これにより各メソッド内でContextクラスを使用することができます。

PostTodoItemメソッドの確認

次にPostTodoItemメソッドを見てみましょう。

...   
        // POST: api/TodoItems
        [HttpPost]
        public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
        {
            // POSTされたデータをContextクラスに追加する
            _context.TodoItems.Add(todoItem);

            // Contextクラスに追加されたデータをDBに登録する(INSERT文が実行される)
            await _context.SaveChangesAsync();

            // ステータスコード201(Created)を返し、GetTodoItemメソッドを呼び出す
            return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
        }
...

POSTメソッドでリクエストされた内容をDBに登録して、登録した内容をレスポンスとして返すメソッドです。このメソッドの動作を確認してみましょう。

APIのテストはPostmanなどのツールを使うことが多いですが、ここではチュートリアルで使われている「HttpRepl」という、.NET Core上で動作するコマンドラインツールを使用します。

HttpReplのインストール

「ツール」→「コマンドライン」→「開発者コマンドプロンプト」でコマンドプロンプトを開きます。

以下のコマンドを実行してください。

dotnet tool install -g Microsoft.dotnet-httprepl

次のように表示されればインストール成功です。

PostTodoItemメソッドの動作確認

まずF5でアプリを起動し、コマンドプロンプト上で以下のコマンドを入力してください。
<ポート番号>にはブラウザのアドレスバーに表示されているポート番号を入力します。

httprepl https://localhost:<ポート番号>/api/todoitems
post -h Content-Type=application/json -c "{"name":"walk dog","isComplete":true}"

このコマンドで、「https://localhost:<ポート番号>/api/todoitems」宛に、 “{“name”:”walk dog”,”isComplete”:true}” という内容のJSONデータを、POSTメソッドでリクエストすることができます。

実行すると次のように表示され、リクエストしたToDoアイテムが登録されたことがわかります。

GetTodoItemメソッドの確認

次に2つのGETメソッドについて確認してみます。

エンドポイント「GET: api/TodoItems」はリクエストパラメータの指定がない場合に全件を返し、 「GET: api/TodoItems/{id}」 はIDの指定がある場合に一致するTodoアイテムを返します。

...   
        // GET: api/TodoItems
        [HttpGet]
        public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
        {
            // DB内のToDoデータを全て取得して返す
            return await _context.TodoItems.ToListAsync();
        }

        // GET: api/TodoItems/5
        [HttpGet("{id}")]
        public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
        {
            // リクエストされたIDと一致するDB内のTodoアイテムを取得
            var todoItem = await _context.TodoItems.FindAsync(id);

            if (todoItem == null)
            {
                // 404 Not Found
                return NotFound();
            }

            // 200 OK
            return todoItem;
        }
...

「GET: api/TodoItems/{id}」 については先ほどのPostTodoItemメソッドで確認できたので、 「GET: api/TodoItems」 の動作について確認してみます。

APIに接続している状態で「get」コマンドを実行することで、GETリクエストを送付できます。
(もし先ほどのコネクションを切断してしまった場合は、「connect https://localhost:<ポート番号>/api/todoitems」で再度APIに接続してください。)

実行すると、先ほどPOSTで登録したアイテムが表示されます。

PutTodoItemメソッドの確認

次はPutTodoItemメソッドです。

PostTodoItemメソッドと似ていますが、一般的にPOSTが新規登録に使われるのに対して、PUTは更新に使われます。また、成功時のレスポンスのステータスはPOSTが「200 OK」ですが、PUTは「204 No Content」となります。

...   
        [HttpPut("{id}")]
        public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
        {
            if (id != todoItem.Id)
            {
                return BadRequest();
            }

            // エンティティ(todoItem)の状態をUnchangedからModifiedに変更する
            _context.Entry(todoItem).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            // 同時実行制御に関する例外(更新中に別のユーザーによって値が書き換えられた場合など)
            catch (DbUpdateConcurrencyException)
            {
                if (!TodoItemExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }
...

EFCoreで更新をする際には、エンティティの状態を変更してからSaveChangesメソッドを実行する流れになります。

以下のコマンドを実行して、ID=1のアイテムの名前を”walk dog”から”feed fish”に更新します。

connect https://localhost:<ポート番号>/api/todoitems/1
put -h Content-Type=application/json -c "{"id":1,"name":"feed fish","isComplete":true}"

「204 No Content」と表示されれば成功です。
「get」コマンドを実行すると、nameが変更されていることが確認できます。

DeleteTodoItemメソッドの確認

最後にDeleteメソッドを確認します。

...   
       // DELETE: api/TodoItems/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteTodoItem(long id)
        {
            var todoItem = await _context.TodoItems.FindAsync(id);
            if (todoItem == null)
            {
                return NotFound();
            }

            // Removeメソッドで該当のTodoアイテムを削除する
            _context.TodoItems.Remove(todoItem);
            await _context.SaveChangesAsync();

            return NoContent();
        }
...

以下のコマンドを実行して、id=1のアイテムを削除してみましょう。

connect https://localhost:<ポート番号>/api/todoitems/1
delete

「No Content」と表示されれば成功です。
getコマンドを実行すると、何も表示されないことが確認できます。

おわりに

とてもシンプルではありましたが、CRUD機能を備えたAPIの作成と確認を行いました。

ASP.NET Core Web APIを使えばとても簡単にAPIアプリを作成できるので、ぜひ様々なAPIを作成してみてください。

最後に、さらに学びたい方に向けておすすめの書籍や記事を紹介します。

Web APIについて基本からしっかり学びたい方

著:水野 貴明
¥2,420 (2023/09/29 15:34時点 | Amazon調べ)

ASP.NET Core MVCを学びたい方

フロントエンドにReactを使ったアプリを作ってみたい方

コメント

タイトルとURLをコピーしました