ASP.NET Core Web APIを初めて触る人でも理解できるように、公式チュートリアルを丁寧に解説してみました。
ToDoアイテムを管理する簡単なAPIを作成していきます。
公式チュートリアルはこちら

本記事の対象
- ASP.NET CoreでWeb APIを作ってみたい人
- C#、Web APIの基本的な知識がある人
環境
- Windows10
- Visual Studio Community 2022
プロジェクトの作成と実行
Visual Studioを起動したら、新規プロジェクトの作成画面を開き、「ASP.NET Core Web API」を選択します。

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

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

プロジェクトの作成が完了したら、「Ctrl+F5」でデバッグなしで実行してみましょう。
次の画面が表示された場合は「はい」をクリックし、その後のセキュリティ警告画面でも「はい」をクリックして証明書をインストールします。

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

Swagger UIとは
SwaggerとはAPIの仕様を管理するフレームワークで、Swagger UIはそれを構成するツールの1つです。 Swagger UIを使うと、設定されたAPIのドキュメントをWebベースで見やすく表示してくれます。ページ上からリクエストを送信して結果を確認することもできます。
ASP.NET Coreでは「Swashbuckle」というパッケージをインストールすることで使用することができ、.NET5以降では標準でインストールされています。(プロジェクト作成時に「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クラスを作成しますが、その前にEntityFrameworkCoreをインストールしておきましょう。
現段階では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を作成してみてください。
最後に、さらに学びたい方に向けておすすめの書籍や記事を紹介します。
コメント