記事内に広告が含まれています

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

C#

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

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

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

本記事の対象

  • ASP.NET Core Web APIの基本的な仕組みを理解したい
  • C#、REST APIの基本的な知識がある

環境

  • Windows 11
  • Visual Studio 2022
  • .NET 8

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

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

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

ターゲットフレームワークには「.NET 8.0(長期的なサポート)」を選択します。その他の項目はデフォルトのままでOKです。

プロジェクトの作成が完了したら、「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 APIの中身を実装していきます。

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

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

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

Idにはテーブルの主キーが格納されます。IsCompleteはタスクが完了したかどうかを表すフラグです。

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

プロジェクト名を右クリック→「NuGet パッケージの管理」を選択してNuGetパッケージマネージャーを開き、「参照」タブから「Microsoft.EntityFrameworkCore.InMemory」と検索します。

表示されたパッケージを選択して、インストールをクリックしてください。

インストールが完了したら、Modelsフォルダ内にTodoContext.csというクラスを追加してください。

using Microsoft.EntityFrameworkCore;

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);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

AddDbContextTodoContextクラスをアプリに登録しています。

UseInMemoryDatabaseは、TodoContextクラスがメモリ内のDBを使用することを指示するもので、実際のDBを用意せずにCRUD処理を実装する際に使用します。

コントローラーのスキャフォールディング

スキャフォールディングを実行すると、各CRUDメソッドが実装されたコントローラーを自動で生成することができます。早速実行してみましょう。

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

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

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

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

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リクエストの処理に役立つメソッドなどが多く含まれています。
  • DbContextクラスの挿入
    作成したTodoContextクラスは、DIを使用してControllerに挿入されています。これにより各メソッド内でDbContextクラスを使用することができます。

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」という公式のコマンドラインツールを使用します。

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
            {
                // 変更のあったプロパティに対してUPDATE文が実行される
                await _context.SaveChangesAsync();
            }
            // 同時実行制御に関する例外(更新中に別のユーザーによって値が書き換えられた場合など)
            catch (DbUpdateConcurrencyException)
            {
                if (!TodoItemExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }
...

EF Coreでデータを更新をする際には、エンティティの状態を変更してからSaveChangesメソッドを実行します。

EF Coreが自動で変更されたプロパティを検知して、変更対象のデータのみに対してUPDATE文が実行されます。

以下のコマンドを実行して、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を作成してみてください。

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

ASP.NET Core の基本的な仕組みを知りたい

Program.csに記載されている内容を中心に、ASP.NET Core の裏側の動きについて解説しています。

ASP.NET Core MVCを学びたい

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

コメント

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