チュートリアル: ASP.NET Core で Web API を作成する
Note
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
警告
このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
重要
この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。
現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
作成者: Rick Anderson および Kirk Larkin
このチュートリアルでは、データベースを使用するコントローラー ベースの Web API の構築の基本について説明します。 ASP.NET Core で API を作成するもう 1 つの方法は、"最小 API" を作成することです。 最小 API とコントローラー ベースの API の選択に関するヘルプについては、API の概要に関する記事をご覧ください。 最小 API の作成に関するチュートリアルについては、「チュートリアル: ASP.NET Core を使って最小 API を作成する」をご覧ください。
概要
このチュートリアルでは、次の API を作成します。
API | 説明 | 要求本文 | 応答本文 |
---|---|---|---|
GET /api/todoitems |
すべての To Do アイテムを取得します。 | None | To Do アイテムの配列 |
GET /api/todoitems/{id} |
ID でアイテムを取得します。 | None | To Do アイテム |
POST /api/todoitems |
新しいアイテムを追加します。 | To Do アイテム | To Do アイテム |
PUT /api/todoitems/{id} |
既存のアイテムを更新します。 | To Do アイテム | None |
DELETE /api/todoitems/{id} |
アイテムを削除します。 | None | None |
次の図は、アプリのデザインを示しています。
前提条件
Visual Studio 2022 と ASP.NET と Web 開発ワークロード。
Web プロジェクトの作成
- [ファイル] メニューで [新規作成]>[プロジェクト] の順に選択します。
- 検索ボックスに「Web API」と入力します。
- [ASP.NET Core Web API] テンプレートを選択し、 [次へ] を選択します。
- [新しいプロジェクトの構成] ダイアログで、プロジェクトに TodoApi という名前を付けて、[次へ] を選択します。
- [追加情報] ダイアログで、次を行います。
- [フレームワーク] が [.NET 8.0 (長期的なサポート)] になっていることを確認します。
- [コントローラーを使用する (最小限の API を使用する場合はオフにします)] チェック ボックスがオンになっていることを確認します。
- [Enable OpenAPI support] (OpenAPI サポートを有効にする) のチェックボックスがオンになっていることを確認します。
- [作成] を選択します
NuGet パッケージの追加
このチュートリアルで使うデータベースをサポートするには、NuGet パッケージを追加する必要があります。
- [ツール] メニューで [NuGet パッケージ マネージャー] > [ソリューションの NuGet パッケージの管理] の順に選択します。
- [参照] タブを選択します。
- 検索ボックスに「Microsoft.EntityFrameworkCore.InMemory」と入力し、
Microsoft.EntityFrameworkCore.InMemory
を選択します。 - 右側のウィンドウで [プロジェクト] チェックボックスをオンにして、 [インストール] を選択します。
Note
.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。
プロジェクトをテストする
プロジェクト テンプレートにより、Swagger をサポートする WeatherForecast
API が作成されます。
Ctrl + F5 キーを押して、デバッガーなしで実行します。
SSL を使用するようにプロジェクトがまだ構成されていない場合、Visual Studio に次のダイアログが表示されます。
IIS Express SSL 証明書を信頼する場合、[はい] を選択します。
次のダイアログが表示されます。
開発証明書を信頼することに同意する場合は、 [はい] を選択します。
Firefox ブラウザーを信頼する方法の詳細については、「Firefox SEC_ERROR_INADEQUATE_KEY_USAGE 証明書エラー」を参照してください。
Visual Studio で既定のブラウザーが起動し、https://localhost:<port>/swagger/index.html
にアクセスします。ここで、<port>
は、プロジェクト作成時に設定したランダムに選択されたポート番号になります。
Swagger ページ /swagger/index.html
が表示されます。 [取得]>[試してみる]>[実行] を選択します。 ページに以下が表示されます。
- WeatherForecast API をテストするための Curl コマンド。
- WeatherForecast API をテストする URL。
- 応答コード、本文、およびヘッダー。
- メディアの種類と、値とスキーマの例を含むドロップダウン リスト ボックス。
Swagger ページが表示されない場合は、こちらの GitHub イシューを参照してください。
Swagger は、Web API の有用なドキュメントやヘルプ ページを生成するために使用されます。 このチュートリアルでは、Swagger を使ってアプリをテストします。 Swagger の詳細については、「Swagger/OpenAPI を使用する ASP.NET Core Web API のドキュメント」を参照してください。
ブラウザーで要求 URL をコピーして貼り付けます: https://localhost:<port>/weatherforecast
次の例のような JSON が返されます。
[
{
"date": "2019-07-16T19:04:05.7257911-06:00",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
},
{
"date": "2019-07-17T19:04:05.7258461-06:00",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
},
{
"date": "2019-07-18T19:04:05.7258467-06:00",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
},
{
"date": "2019-07-19T19:04:05.7258471-06:00",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
},
{
"date": "2019-07-20T19:04:05.7258474-06:00",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
}
]
モデル クラスの追加
モデルは、アプリが管理するデータを表すクラスのセットです。 このアプリのモデルは、TodoItem
クラスです。
- ソリューション エクスプローラーで、プロジェクトを右クリックします。 [追加]>[新しいフォルダー] の順に選択します。 フォルダーに「
Models
で行うことができます。 Models
フォルダーを右クリックして、[追加]>[クラス] の順に選択します。 クラスに「TodoItem」という名前を付け、 [追加] を選択します。- テンプレート コードを次のコードに置き換えます。
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Id
プロパティは、リレーショナル データベース内の一意のキーとして機能します。
モデル クラスはプロジェクト内のどこでも使用できますが、慣例により Models
フォルダーが使用されます。
データベース コンテキストの追加
データベース コンテキストは、データ モデルに対して Entity Framework 機能を調整するメイン クラスです。 このクラスは Microsoft.EntityFrameworkCore.DbContext クラスから派生させて作成します。
Models
フォルダーを右クリックして、[追加]>[クラス] の順に選択します。 クラスに「TodoContext」という名前を付け、 [追加] をクリックします。
次のコードを入力します。
using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; public class TodoContext : DbContext { public TodoContext(DbContextOptions<TodoContext> options) : base(options) { } public DbSet<TodoItem> TodoItems { get; set; } = null!; }
データベース コンテキストの登録
ASP.NET Core で、サービス (DB コンテキストなど) を依存関係の挿入 (DI)コンテナーに登録する必要があります。 コンテナーは、コントローラーにサービスを提供します。
次の強調表示されているコードを使用して、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.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
上記のコードでは次の操作が行われます。
using
ディレクティブを追加します。- DI コンテナーにデータベース コンテキストを追加します。
- データベース コンテキストがメモリ内データベースを使用することを指定します。
コントローラーのスキャフォールディング
Controllers
フォルダーを右クリックしします。[追加]>New Scaffolded Item を選択します。
[Entity Framework を使用したアクションがある API コントローラー] を選択してから、 [追加] を選択します。
[Entity Framework を使用したアクションがある API コントローラー] ダイアログで次を実行します。
- モデル クラスで [TodoItem (TodoApi.Models)] を選択します。
- データ コンテキスト クラスで [TodoContext (TodoApi.Models)] を選択します。
- [追加] を選びます。
スキャフォールディング操作が失敗した場合は、 [追加] を選択して、もう一度スキャフォールディングを試行します。
生成されたコードでは次の操作が行われます。
- クラスを
[ApiController]
属性でマークします。 この属性は、コントローラーが Web API 要求に応答することを示します。 属性によって有効化される特定の動作については、「ASP.NET Core を使って Web API を作成する」を参照してください。 - DI を使用して、データベース コンテキスト (
TodoContext
) をコントローラーに挿入します。 データベース コンテキストは、コントローラーの各 CRUD メソッドで使用されます。
ASP.NET Core テンプレートの対象は次のとおりです。
- ビューを含むコントローラーには、ルート テンプレートの
[action]
が含まれます。 - API コントローラーには、ルート テンプレートの
[action]
が含まれません。
[action]
トークンがルート テンプレートにない場合、アクション名 (メソッド名) はエンドポイントに含まれません。 つまり、アクションの関連付けられたメソッド名は一致するルートでは使用されません。
PostTodoItem 作成メソッドの更新
nameof 演算子を使用するために、PostTodoItem
で return ステートメントを更新します。
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
[HttpPost]
属性が示すように、上記のコードは HTTP POST
メソッドです。 このメソッドは、HTTP 要求の本文から TodoItem
の値を取得します。
詳細については、「Http[Verb] 属性を使用する属性ルーティング」を参照してください。
CreatedAtAction メソッド:
- 成功すると、HTTP 201 状態コードが返されます。
HTTP 201
は、サーバーに新しいリソースを作成するHTTP POST
メソッドに対する標準の応答です。 - 応答に Location ヘッダーが追加されます。
Location
ヘッダーでは、新しく作成された To Do アイテムの URI が指定されます。 詳細については、「10.2.2 201 Created」を参照してください。 GetTodoItem
アクションを参照してLocation
ヘッダーの URI を作成します。 C# のnameof
キーワードを使って、CreatedAtAction
呼び出しでアクション名をハードコーディングすることを回避しています。
PostTodoItem のテスト
Ctrl キーを押しながら F5 キーを押して、アプリを実行します。
Swagger ブラウザー ウィンドウで、[POST /api/TodoItems] を選択し、次に [試してみる] を選択します。
要求本文の入力ウィンドウで、JSON を更新します。 たとえば、 にします。
{ "name": "walk dog", "isComplete": true }
[実行] を選択します
場所ヘッダー URI のテスト
上記の POST では、Swagger UI の [応答ヘッダー] に場所ヘッダーが表示されています。 たとえば、「 location: https://localhost:7260/api/TodoItems/1
」のように入力します。 場所ヘッダーには、作成されたリソースへの URI が表示されます。
場所ヘッダーをテストするには:
Swagger ブラウザー ウィンドウで、[GET /api/TodoItems/{id}] を選択し、次に [試してみる] を選択します。
id
入力ボックスに1
を入力し、[実行] を選択します。
GET メソッドの確認
2 つの GET エンドポイントが実装されます。
GET /api/todoitems
GET /api/todoitems/{id}
前のセクションでは、/api/todoitems/{id}
ルートの例を示しました。
POST の指示に従って別の todo 項目を追加し、Swagger を使用して /api/todoitems
ルートをテストします。
このアプリではメモリ内データベースが使用されます。 アプリが停止して開始された場合、上記の GET 要求はどのようなデータも返しません。 データが返されない場合は、アプリにデータを POST します。
ルーティングと URL パス
[HttpGet]
属性は、HTTP GET
要求に応答するメソッドを表します。 各メソッドの URL パスは次のように構成されます。
コントローラーの
Route
属性でテンプレート文字列を使用します。[Route("api/[controller]")] [ApiController] public class TodoItemsController : ControllerBase
[controller]
をコントローラーの名前 (慣例では "Controller" サフィックスを除くコントローラー クラス名) に置き換えます。 このサンプルでは、コントローラー クラス名は TodoItemsController なので、コントローラー名は "TodoItems" です。 ASP.NET Core のルーティングでは、大文字と小文字が区別されません。[HttpGet]
属性にルート テンプレート (たとえば、[HttpGet("products")]
) がある場合は、それをパスに追加します。 このサンプルではテンプレートを使用しません。 詳細については、「Http[Verb] 属性を使用する属性ルーティング」を参照してください。
次の GetTodoItem
メソッドで、"{id}"
は To Do アイテムの一意識別子に使用するプレースホルダーの変数です。 GetTodoItem
が呼び出されると、その id
パラメーター内のメソッドに URL の "{id}"
の値が指定されます。
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
戻り値
GetTodoItems
メソッドと GetTodoItem
メソッドの戻り値の型は、ActionResult<T> 型です。 ASP.NET Core は自動的にオブジェクトを JSON にシリアル化して、応答メッセージの本文に JSON を書き込みます。 ハンドルされない例外がないと仮定すると、この戻り値の型の応答コードは 200 OK です。 ハンドルされない例外は 5xx エラーに変換されます。
ActionResult
戻り値の型は、幅広い範囲の HTTP 状態コードを表すことができます。 たとえば、GetTodoItem
は、次の 2 つの異なる状態値を返す可能性があります。
- 要求された ID に一致するアイテムがない場合、このメソッドにより 404 ステータス NotFound エラー コードが返されます。
- それ以外の場合、メソッドは JSON 応答本文で 200 を返します。 戻り値が
item
の場合、HTTP 200
応答が返されます。
PutTodoItem メソッド
PutTodoItem
メソッドを検証します。
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
if (id != todoItem.Id)
{
return BadRequest();
}
_context.Entry(todoItem).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
PutTodoItem
は PostTodoItem
と似ていますが、HTTP PUT
を使用します。 応答は 204 (No Content) となります。 HTTP 仕様に従って、PUT
要求では、変更だけでなく、更新されたエンティティ全体を送信するようクライアントに求めます。 部分的な更新をサポートするには、HTTP PATCH を使用します。
PutTodoItem メソッドのテスト
このサンプルでは、アプリを起動するたびに開始することが必要なメモリ内データベースが使われています。 PUT 呼び出しを実行する前に、データベース内にアイテムが存在している必要があります。 GET を呼び出して、PUT 呼び出しを実行する前にデータベース内にアイテムが確実に存在していることを確認します。
Swagger UI を使用して、PUT ボタンを使用して Id = 1 の TodoItem
を更新し、その名前を "feed fish"
に設定します。 応答は HTTP 204 No Content
であることに注意してください。
DeleteTodoItem メソッド
DeleteTodoItem
メソッドを検証します。
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
DeleteTodoItem メソッドのテスト
Swagger UI を使用して、Id = 1 の TodoItem
を削除します。 応答は HTTP 204 No Content
であることに注意してください。
他のツールでテストする
Web API のテストには、他にも使用できるツールが多数あります。次に例を示します。
- Visual Studio Endpoints Explorer と .http ファイル
- http-repl
- curl。 Swagger は
curl
を使い、送信されたcurl
コマンドを表示します。 - Fiddler
詳細については、次を参照してください。
過剰な投稿を防止する
現在、サンプル アプリでは TodoItem
オブジェクト全体が公開されています。 通常、運用環境のアプリでは、モデルのサブセットを使用して入力されるデータおよび返されるデータが制限されています。 その背景には複数の理由があり、セキュリティは主なものです。 モデルのサブセットは、通常、データ転送オブジェクト (DTO)、入力モデル、またはビュー モデルと呼ばれます。 このチュートリアルでは DTO を使用しています。
DTO は次の目的で使用できます。
- 過剰な投稿を防止する。
- クライアントが表示しないことになっているプロパティを非表示にする。
- ペイロード サイズを減らすために、いくつかのプロパティを省略する。
- 入れ子になったオブジェクトを含むオブジェクト グラフをフラット化する。 フラット化されたオブジェクト グラフは、クライアントにとってより便利になる可能性があります。
DTO のアプローチを実演するために、TodoItem
クラスを更新して、シークレット フィールドを含めます。
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
}
シークレット フィールドは、このアプリでは非表示にする必要がありますが、管理アプリの場合は公開することを選択できます。
シークレット フィールドを投稿および取得できることを確認します。
次のように DTO モデルを作成します。
namespace TodoApi.Models;
public class TodoItemDTO
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
TodoItemDTO
を使用するように TodoItemsController
を更新します。
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
{
_context = context;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
}
// GET: api/TodoItems/5
// <snippet_GetByID>
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return ItemToDTO(todoItem);
}
// </snippet_GetByID>
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Update>
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
{
if (id != todoDTO.Id)
{
return BadRequest();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
todoItem.Name = todoDTO.Name;
todoItem.IsComplete = todoDTO.IsComplete;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
return NoContent();
}
// </snippet_Update>
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Create>
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoDTO.IsComplete,
Name = todoDTO.Name
};
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
}
// </snippet_Create>
// 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();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
private bool TodoItemExists(long id)
{
return _context.TodoItems.Any(e => e.Id == id);
}
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
シークレット フィールドを投稿または取得できないことを確認します。
JavaScript を使用した Web API の呼び出し
「チュートリアル:JavaScript を使用して ASP.NET Core Web API を呼び出す」を参照してください。
Web API ビデオ シリーズ
ビデオ: 「ビギナーズ シリーズ: Web API」を参照してください。
信頼性の高い Web アプリ パターン
ゼロから、または既存のアプリをリファクタリングして、信頼性とパフォーマンスが高く、テスト可能で、コスト効率が高く、スケーラブルな最新の ASP.NET Core アプリを作成する方法のガイダンスについては、"信頼性の高い .NET の Web アプリ パターン" に関する YouTube のビデオと記事をご覧ください。
Web API に認証サポートを追加
ASP.NET Core Identity では、ASP.NET Core Web アプリにユーザー インターフェイス (UI) ログイン機能が追加されます。 Web API と SPA をセキュリティで保護するには、次のいずれかを使用します。
- Microsoft Entra ID
- Azure Active Directory B2C (Azure AD B2C)
- Duende Identity Server
Duende Identity Server は、ASP.NET Core 用の OpenID Connect および OAuth 2.0 フレームワークです。 Duende Identity Server により、次のセキュリティ機能が有効になります。
- サービスとしての認証 (AaaS)
- 複数のアプリケーションの種類でのシングル サインオン/オフ (SSO)
- API のアクセス制御
- Federation Gateway
重要
Duende Software により、Duende Identity Server を実稼働で使用することのライセンス料の支払いが求められる場合があります。 詳細については、「ASP.NET Core 5.0 から 6.0 への移行」を参照してください。
詳細については、Duende Identity Server に関するドキュメント (Duende ソフトウェアの Web サイト) を参照してください。
Azure に発行する
Azure へのデプロイについては、「クイックスタート: ASP.NET Web アプリをデプロイする」を参照してください。
その他の技術情報
このチュートリアルのサンプル コードを表示またはダウンロードします。 ダウンロード方法に関するページを参照してください。
詳細については、次のリソースを参照してください。
- ASP.NET Core を使って Web API を作成する
- チュートリアル: ASP.NET Core を使って最小 API を作成する
- Swagger/OpenAPI を使用する ASP.NET Core Web API のドキュメント
- RazorASP.NET Core での Entity Framework Core を使用した Pages - チュートリアル 1/8
- ASP.NET Core でのコントローラー アクションへのルーティング
- ASP.NET Core Web API のコントローラー アクションの戻り値の型
- Azure App Service に ASP.NET Core アプリを展開する
- ASP.NET Core のホストと展開
- ASP.NET Core で Web API を作成する
このチュートリアルでは、データベースを使用するコントローラー ベースの Web API の構築の基本について説明します。 ASP.NET Core で API を作成するもう 1 つの方法は、"最小 API" を作成することです。 最小 API とコントローラー ベースの API の選択に関するヘルプについては、API の概要に関する記事をご覧ください。 最小 API の作成に関するチュートリアルについては、「チュートリアル: ASP.NET Core を使って最小 API を作成する」をご覧ください。
概要
このチュートリアルでは、次の API を作成します。
API | 説明 | 要求本文 | 応答本文 |
---|---|---|---|
GET /api/todoitems |
すべての To Do アイテムを取得します。 | None | To Do アイテムの配列 |
GET /api/todoitems/{id} |
ID でアイテムを取得します。 | None | To Do アイテム |
POST /api/todoitems |
新しいアイテムを追加します。 | To Do アイテム | To Do アイテム |
PUT /api/todoitems/{id} |
既存のアイテムを更新します。 | To Do アイテム | None |
DELETE /api/todoitems/{id} |
アイテムを削除します。 | None | None |
次の図は、アプリのデザインを示しています。
前提条件
Visual Studio 2022 と ASP.NET と Web 開発ワークロード。
Web プロジェクトの作成
- [ファイル] メニューで [新規作成]>[プロジェクト] の順に選択します。
- 検索ボックスに「Web API」と入力します。
- [ASP.NET Core Web API] テンプレートを選択し、 [次へ] を選択します。
- [新しいプロジェクトの構成] ダイアログで、プロジェクトに TodoApi という名前を付けて、[次へ] を選択します。
- [追加情報] ダイアログで、次を行います。
- [フレームワーク] が [.NET 8.0 (長期的なサポート)] になっていることを確認します。
- [コントローラーを使用する (最小限の API を使用する場合はオフにします)] チェック ボックスがオンになっていることを確認します。
- [Enable OpenAPI support] (OpenAPI サポートを有効にする) のチェックボックスがオンになっていることを確認します。
- [作成] を選択します
NuGet パッケージの追加
このチュートリアルで使うデータベースをサポートするには、NuGet パッケージを追加する必要があります。
- [ツール] メニューで [NuGet パッケージ マネージャー] > [ソリューションの NuGet パッケージの管理] の順に選択します。
- [参照] タブを選択します。
- 検索ボックスに「Microsoft.EntityFrameworkCore.InMemory」と入力し、
Microsoft.EntityFrameworkCore.InMemory
を選択します。 - 右側のウィンドウで [プロジェクト] チェックボックスをオンにして、 [インストール] を選択します。
メモ
.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。
プロジェクトをテストする
プロジェクト テンプレートにより、Swagger をサポートする WeatherForecast
API が作成されます。
Ctrl + F5 キーを押して、デバッガーなしで実行します。
SSL を使用するようにプロジェクトがまだ構成されていない場合、Visual Studio に次のダイアログが表示されます。
IIS Express SSL 証明書を信頼する場合、[はい] を選択します。
次のダイアログが表示されます。
開発証明書を信頼することに同意する場合は、 [はい] を選択します。
Firefox ブラウザーを信頼する方法の詳細については、「Firefox SEC_ERROR_INADEQUATE_KEY_USAGE 証明書エラー」を参照してください。
Visual Studio で既定のブラウザーが起動し、https://localhost:<port>/swagger/index.html
にアクセスします。ここで、<port>
は、プロジェクト作成時に設定したランダムに選択されたポート番号になります。
Swagger ページ /swagger/index.html
が表示されます。 [取得]>[試してみる]>[実行] を選択します。 ページに以下が表示されます。
- WeatherForecast API をテストするための Curl コマンド。
- WeatherForecast API をテストする URL。
- 応答コード、本文、およびヘッダー。
- メディアの種類と、値とスキーマの例を含むドロップダウン リスト ボックス。
Swagger ページが表示されない場合は、こちらの GitHub イシューを参照してください。
Swagger は、Web API の有用なドキュメントやヘルプ ページを生成するために使用されます。 このチュートリアルでは、Swagger を使ってアプリをテストします。 Swagger の詳細については、「Swagger/OpenAPI を使用する ASP.NET Core Web API のドキュメント」を参照してください。
ブラウザーで要求 URL をコピーして貼り付けます: https://localhost:<port>/weatherforecast
次の例のような JSON が返されます。
[
{
"date": "2019-07-16T19:04:05.7257911-06:00",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
},
{
"date": "2019-07-17T19:04:05.7258461-06:00",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
},
{
"date": "2019-07-18T19:04:05.7258467-06:00",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
},
{
"date": "2019-07-19T19:04:05.7258471-06:00",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
},
{
"date": "2019-07-20T19:04:05.7258474-06:00",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
}
]
モデル クラスの追加
モデルは、アプリが管理するデータを表すクラスのセットです。 このアプリのモデルは、TodoItem
クラスです。
- ソリューション エクスプローラーで、プロジェクトを右クリックします。 [追加]>[新しいフォルダー] の順に選択します。 フォルダーに「
Models
で行うことができます。 Models
フォルダーを右クリックして、[追加]>[クラス] の順に選択します。 クラスに「TodoItem」という名前を付け、 [追加] を選択します。- テンプレート コードを次のコードに置き換えます。
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Id
プロパティは、リレーショナル データベース内の一意のキーとして機能します。
モデル クラスはプロジェクト内のどこでも使用できますが、慣例により Models
フォルダーが使用されます。
データベース コンテキストの追加
データベース コンテキストは、データ モデルに対して Entity Framework 機能を調整するメイン クラスです。 このクラスは Microsoft.EntityFrameworkCore.DbContext クラスから派生させて作成します。
Models
フォルダーを右クリックして、[追加]>[クラス] の順に選択します。 クラスに「TodoContext」という名前を付け、 [追加] をクリックします。
次のコードを入力します。
using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; public class TodoContext : DbContext { public TodoContext(DbContextOptions<TodoContext> options) : base(options) { } public DbSet<TodoItem> TodoItems { get; set; } = null!; }
データベース コンテキストの登録
ASP.NET Core で、サービス (DB コンテキストなど) を依存関係の挿入 (DI)コンテナーに登録する必要があります。 コンテナーは、コントローラーにサービスを提供します。
次の強調表示されているコードを使用して、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.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
上記のコードでは次の操作が行われます。
using
ディレクティブを追加します。- DI コンテナーにデータベース コンテキストを追加します。
- データベース コンテキストがメモリ内データベースを使用することを指定します。
コントローラーのスキャフォールディング
Controllers
フォルダーを右クリックしします。[追加]>New Scaffolded Item を選択します。
[Entity Framework を使用したアクションがある API コントローラー] を選択してから、 [追加] を選択します。
[Entity Framework を使用したアクションがある API コントローラー] ダイアログで次を実行します。
- モデル クラスで [TodoItem (TodoApi.Models)] を選択します。
- データ コンテキスト クラスで [TodoContext (TodoApi.Models)] を選択します。
- [追加] を選びます。
スキャフォールディング操作が失敗した場合は、 [追加] を選択して、もう一度スキャフォールディングを試行します。
生成されたコードでは次の操作が行われます。
- クラスを
[ApiController]
属性でマークします。 この属性は、コントローラーが Web API 要求に応答することを示します。 属性によって有効化される特定の動作については、「ASP.NET Core を使って Web API を作成する」を参照してください。 - DI を使用して、データベース コンテキスト (
TodoContext
) をコントローラーに挿入します。 データベース コンテキストは、コントローラーの各 CRUD メソッドで使用されます。
ASP.NET Core テンプレートの対象は次のとおりです。
- ビューを含むコントローラーには、ルート テンプレートの
[action]
が含まれます。 - API コントローラーには、ルート テンプレートの
[action]
が含まれません。
[action]
トークンがルート テンプレートにない場合、アクション名 (メソッド名) はエンドポイントに含まれません。 つまり、アクションの関連付けられたメソッド名は一致するルートでは使用されません。
PostTodoItem 作成メソッドの更新
nameof 演算子を使用するために、PostTodoItem
で return ステートメントを更新します。
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
[HttpPost]
属性が示すように、上記のコードは HTTP POST
メソッドです。 このメソッドは、HTTP 要求の本文から TodoItem
の値を取得します。
詳細については、「Http[Verb] 属性を使用する属性ルーティング」を参照してください。
CreatedAtAction メソッド:
- 成功すると、HTTP 201 状態コードが返されます。
HTTP 201
は、サーバーに新しいリソースを作成するHTTP POST
メソッドに対する標準の応答です。 - 応答に Location ヘッダーが追加されます。
Location
ヘッダーでは、新しく作成された To Do アイテムの URI が指定されます。 詳細については、「10.2.2 201 Created」を参照してください。 GetTodoItem
アクションを参照してLocation
ヘッダーの URI を作成します。 C# のnameof
キーワードを使って、CreatedAtAction
呼び出しでアクション名をハードコーディングすることを回避しています。
PostTodoItem のテスト
Ctrl キーを押しながら F5 キーを押して、アプリを実行します。
Swagger ブラウザー ウィンドウで、[POST /api/TodoItems] を選択し、次に [試してみる] を選択します。
要求本文の入力ウィンドウで、JSON を更新します。 たとえば、 にします。
{ "name": "walk dog", "isComplete": true }
[実行] を選択します
場所ヘッダー URI のテスト
上記の POST では、Swagger UI の [応答ヘッダー] に場所ヘッダーが表示されています。 たとえば、「 location: https://localhost:7260/api/TodoItems/1
」のように入力します。 場所ヘッダーには、作成されたリソースへの URI が表示されます。
場所ヘッダーをテストするには:
Swagger ブラウザー ウィンドウで、[GET /api/TodoItems/{id}] を選択し、次に [試してみる] を選択します。
id
入力ボックスに1
を入力し、[実行] を選択します。
GET メソッドの確認
2 つの GET エンドポイントが実装されます。
GET /api/todoitems
GET /api/todoitems/{id}
前のセクションでは、/api/todoitems/{id}
ルートの例を示しました。
POST の指示に従って別の todo 項目を追加し、Swagger を使用して /api/todoitems
ルートをテストします。
このアプリではメモリ内データベースが使用されます。 アプリが停止して開始された場合、上記の GET 要求はどのようなデータも返しません。 データが返されない場合は、アプリにデータを POST します。
ルーティングと URL パス
[HttpGet]
属性は、HTTP GET
要求に応答するメソッドを表します。 各メソッドの URL パスは次のように構成されます。
コントローラーの
Route
属性でテンプレート文字列を使用します。[Route("api/[controller]")] [ApiController] public class TodoItemsController : ControllerBase
[controller]
をコントローラーの名前 (慣例では "Controller" サフィックスを除くコントローラー クラス名) に置き換えます。 このサンプルでは、コントローラー クラス名は TodoItemsController なので、コントローラー名は "TodoItems" です。 ASP.NET Core のルーティングでは、大文字と小文字が区別されません。[HttpGet]
属性にルート テンプレート (たとえば、[HttpGet("products")]
) がある場合は、それをパスに追加します。 このサンプルではテンプレートを使用しません。 詳細については、「Http[Verb] 属性を使用する属性ルーティング」を参照してください。
次の GetTodoItem
メソッドで、"{id}"
は To Do アイテムの一意識別子に使用するプレースホルダーの変数です。 GetTodoItem
が呼び出されると、その id
パラメーター内のメソッドに URL の "{id}"
の値が指定されます。
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
戻り値
GetTodoItems
メソッドと GetTodoItem
メソッドの戻り値の型は、ActionResult<T> 型です。 ASP.NET Core は自動的にオブジェクトを JSON にシリアル化して、応答メッセージの本文に JSON を書き込みます。 ハンドルされない例外がないと仮定すると、この戻り値の型の応答コードは 200 OK です。 ハンドルされない例外は 5xx エラーに変換されます。
ActionResult
戻り値の型は、幅広い範囲の HTTP 状態コードを表すことができます。 たとえば、GetTodoItem
は、次の 2 つの異なる状態値を返す可能性があります。
- 要求された ID に一致するアイテムがない場合、このメソッドにより 404 ステータス NotFound エラー コードが返されます。
- それ以外の場合、メソッドは JSON 応答本文で 200 を返します。 戻り値が
item
の場合、HTTP 200
応答が返されます。
PutTodoItem メソッド
PutTodoItem
メソッドを検証します。
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
if (id != todoItem.Id)
{
return BadRequest();
}
_context.Entry(todoItem).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
PutTodoItem
は PostTodoItem
と似ていますが、HTTP PUT
を使用します。 応答は 204 (No Content) となります。 HTTP 仕様に従って、PUT
要求では、変更だけでなく、更新されたエンティティ全体を送信するようクライアントに求めます。 部分的な更新をサポートするには、HTTP PATCH を使用します。
PutTodoItem メソッドのテスト
このサンプルでは、アプリを起動するたびに開始することが必要なメモリ内データベースが使われています。 PUT 呼び出しを実行する前に、データベース内にアイテムが存在している必要があります。 GET を呼び出して、PUT 呼び出しを実行する前にデータベース内にアイテムが確実に存在していることを確認します。
Swagger UI を使用して、PUT ボタンを使用して Id = 1 の TodoItem
を更新し、その名前を "feed fish"
に設定します。 応答は HTTP 204 No Content
であることに注意してください。
DeleteTodoItem メソッド
DeleteTodoItem
メソッドを検証します。
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
DeleteTodoItem メソッドのテスト
Swagger UI を使用して、Id = 1 の TodoItem
を削除します。 応答は HTTP 204 No Content
であることに注意してください。
他のツールでテストする
Web API のテストには、他にも使用できるツールが多数あります。次に例を示します。
- Visual Studio Endpoints Explorer と .http ファイル
- http-repl
- curl。 Swagger は
curl
を使い、送信されたcurl
コマンドを表示します。 - Fiddler
詳細については、次を参照してください。
過剰な投稿を防止する
現在、サンプル アプリでは TodoItem
オブジェクト全体が公開されています。 通常、運用環境のアプリでは、モデルのサブセットを使用して入力されるデータおよび返されるデータが制限されています。 その背景には複数の理由があり、セキュリティは主なものです。 モデルのサブセットは、通常、データ転送オブジェクト (DTO)、入力モデル、またはビュー モデルと呼ばれます。 このチュートリアルでは DTO を使用しています。
DTO は次の目的で使用できます。
- 過剰な投稿を防止する。
- クライアントが表示しないことになっているプロパティを非表示にする。
- ペイロード サイズを減らすために、いくつかのプロパティを省略する。
- 入れ子になったオブジェクトを含むオブジェクト グラフをフラット化する。 フラット化されたオブジェクト グラフは、クライアントにとってより便利になる可能性があります。
DTO のアプローチを実演するために、TodoItem
クラスを更新して、シークレット フィールドを含めます。
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
}
シークレット フィールドは、このアプリでは非表示にする必要がありますが、管理アプリの場合は公開することを選択できます。
シークレット フィールドを投稿および取得できることを確認します。
次のように DTO モデルを作成します。
namespace TodoApi.Models;
public class TodoItemDTO
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
TodoItemDTO
を使用するように TodoItemsController
を更新します。
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
{
_context = context;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
}
// GET: api/TodoItems/5
// <snippet_GetByID>
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return ItemToDTO(todoItem);
}
// </snippet_GetByID>
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Update>
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
{
if (id != todoDTO.Id)
{
return BadRequest();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
todoItem.Name = todoDTO.Name;
todoItem.IsComplete = todoDTO.IsComplete;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
return NoContent();
}
// </snippet_Update>
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Create>
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoDTO.IsComplete,
Name = todoDTO.Name
};
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
}
// </snippet_Create>
// 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();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
private bool TodoItemExists(long id)
{
return _context.TodoItems.Any(e => e.Id == id);
}
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
シークレット フィールドを投稿または取得できないことを確認します。
JavaScript を使用した Web API の呼び出し
「チュートリアル:JavaScript を使用して ASP.NET Core Web API を呼び出す」を参照してください。
Web API ビデオ シリーズ
ビデオ: 「ビギナーズ シリーズ: Web API」を参照してください。
信頼性の高い Web アプリ パターン
ゼロから、または既存のアプリをリファクタリングして、信頼性とパフォーマンスが高く、テスト可能で、コスト効率が高く、スケーラブルな最新の ASP.NET Core アプリを作成する方法のガイダンスについては、"信頼性の高い .NET の Web アプリ パターン" に関する YouTube のビデオと記事をご覧ください。
Web API に認証サポートを追加
ASP.NET Core Identity では、ASP.NET Core Web アプリにユーザー インターフェイス (UI) ログイン機能が追加されます。 Web API と SPA をセキュリティで保護するには、次のいずれかを使用します。
- Microsoft Entra ID
- Azure Active Directory B2C (Azure AD B2C)
- Duende Identity Server
Duende Identity Server は、ASP.NET Core 用の OpenID Connect および OAuth 2.0 フレームワークです。 Duende Identity Server により、次のセキュリティ機能が有効になります。
- サービスとしての認証 (AaaS)
- 複数のアプリケーションの種類でのシングル サインオン/オフ (SSO)
- API のアクセス制御
- Federation Gateway
重要
Duende Software により、Duende Identity Server を実稼働で使用することのライセンス料の支払いが求められる場合があります。 詳細については、「ASP.NET Core 5.0 から 6.0 への移行」を参照してください。
詳細については、Duende Identity Server に関するドキュメント (Duende ソフトウェアの Web サイト) を参照してください。
Azure に発行する
Azure へのデプロイについては、「クイックスタート: ASP.NET Web アプリをデプロイする」を参照してください。
その他の技術情報
このチュートリアルのサンプル コードを表示またはダウンロードします。 ダウンロード方法に関するページを参照してください。
詳細については、次のリソースを参照してください。
- ASP.NET Core を使って Web API を作成する
- チュートリアル: ASP.NET Core を使って最小 API を作成する
- Swagger/OpenAPI を使用する ASP.NET Core Web API のドキュメント
- RazorASP.NET Core での Entity Framework Core を使用した Pages - チュートリアル 1/8
- ASP.NET Core でのコントローラー アクションへのルーティング
- ASP.NET Core Web API のコントローラー アクションの戻り値の型
- Azure App Service に ASP.NET Core アプリを展開する
- ASP.NET Core のホストと展開
- ASP.NET Core で Web API を作成する
このチュートリアルでは、データベースを使用するコントローラー ベースの Web API の構築の基本について説明します。 ASP.NET Core で API を作成するもう 1 つの方法は、"最小 API" を作成することです。 最小 API とコントローラー ベースの API の選択に関するヘルプについては、API の概要に関する記事をご覧ください。 最小 API の作成に関するチュートリアルについては、「チュートリアル: ASP.NET Core を使って最小 API を作成する」をご覧ください。
概要
このチュートリアルでは、次の API を作成します。
API | 説明 | 要求本文 | 応答本文 |
---|---|---|---|
GET /api/todoitems |
すべての To Do アイテムを取得します。 | None | To Do アイテムの配列 |
GET /api/todoitems/{id} |
ID でアイテムを取得します。 | None | To Do アイテム |
POST /api/todoitems |
新しいアイテムを追加します。 | To Do アイテム | To Do アイテム |
PUT /api/todoitems/{id} |
既存のアイテムを更新します。 | To Do アイテム | None |
DELETE /api/todoitems/{id} |
アイテムを削除します。 | None | None |
次の図は、アプリのデザインを示しています。
前提条件
Visual Studio 2022 と ASP.NET と Web 開発ワークロード。
Web プロジェクトの作成
- [ファイル] メニューで [新規作成]>[プロジェクト] の順に選択します。
- 検索ボックスに「Web API」と入力します。
- [ASP.NET Core Web API] テンプレートを選択し、 [次へ] を選択します。
- [新しいプロジェクトの構成] ダイアログで、プロジェクトに TodoApi という名前を付けて、[次へ] を選択します。
- [追加情報] ダイアログで、次を行います。
- [フレームワーク] が [.NET 8.0 (長期的なサポート)] になっていることを確認します。
- [コントローラーを使用する (最小限の API を使用する場合はオフにします)] チェック ボックスがオンになっていることを確認します。
- [Enable OpenAPI support] (OpenAPI サポートを有効にする) のチェックボックスがオンになっていることを確認します。
- [作成] を選択します
NuGet パッケージの追加
このチュートリアルで使うデータベースをサポートするには、NuGet パッケージを追加する必要があります。
- [ツール] メニューで [NuGet パッケージ マネージャー] > [ソリューションの NuGet パッケージの管理] の順に選択します。
- [参照] タブを選択します。
- 検索ボックスに「Microsoft.EntityFrameworkCore.InMemory」と入力し、
Microsoft.EntityFrameworkCore.InMemory
を選択します。 - 右側のウィンドウで [プロジェクト] チェックボックスをオンにして、 [インストール] を選択します。
Note
.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。
プロジェクトをテストする
プロジェクト テンプレートにより、Swagger をサポートする WeatherForecast
API が作成されます。
Ctrl + F5 キーを押して、デバッガーなしで実行します。
SSL を使用するようにプロジェクトがまだ構成されていない場合、Visual Studio に次のダイアログが表示されます。
IIS Express SSL 証明書を信頼する場合、[はい] を選択します。
次のダイアログが表示されます。
開発証明書を信頼することに同意する場合は、 [はい] を選択します。
Firefox ブラウザーを信頼する方法の詳細については、「Firefox SEC_ERROR_INADEQUATE_KEY_USAGE 証明書エラー」を参照してください。
Visual Studio で既定のブラウザーが起動し、https://localhost:<port>/swagger/index.html
にアクセスします。ここで、<port>
は、プロジェクト作成時に設定したランダムに選択されたポート番号になります。
Swagger ページ /swagger/index.html
が表示されます。 [取得]>[試してみる]>[実行] を選択します。 ページに以下が表示されます。
- WeatherForecast API をテストするための Curl コマンド。
- WeatherForecast API をテストする URL。
- 応答コード、本文、およびヘッダー。
- メディアの種類と、値とスキーマの例を含むドロップダウン リスト ボックス。
Swagger ページが表示されない場合は、こちらの GitHub イシューを参照してください。
Swagger は、Web API の有用なドキュメントやヘルプ ページを生成するために使用されます。 このチュートリアルでは、Swagger を使ってアプリをテストします。 Swagger の詳細については、「Swagger/OpenAPI を使用する ASP.NET Core Web API のドキュメント」を参照してください。
ブラウザーで要求 URL をコピーして貼り付けます: https://localhost:<port>/weatherforecast
次の例のような JSON が返されます。
[
{
"date": "2019-07-16T19:04:05.7257911-06:00",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
},
{
"date": "2019-07-17T19:04:05.7258461-06:00",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
},
{
"date": "2019-07-18T19:04:05.7258467-06:00",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
},
{
"date": "2019-07-19T19:04:05.7258471-06:00",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
},
{
"date": "2019-07-20T19:04:05.7258474-06:00",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
}
]
モデル クラスの追加
モデルは、アプリが管理するデータを表すクラスのセットです。 このアプリのモデルは、TodoItem
クラスです。
- ソリューション エクスプローラーで、プロジェクトを右クリックします。 [追加]>[新しいフォルダー] の順に選択します。 フォルダーに「
Models
で行うことができます。 Models
フォルダーを右クリックして、[追加]>[クラス] の順に選択します。 クラスに「TodoItem」という名前を付け、 [追加] を選択します。- テンプレート コードを次のコードに置き換えます。
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Id
プロパティは、リレーショナル データベース内の一意のキーとして機能します。
モデル クラスはプロジェクト内のどこでも使用できますが、慣例により Models
フォルダーが使用されます。
データベース コンテキストの追加
データベース コンテキストは、データ モデルに対して Entity Framework 機能を調整するメイン クラスです。 このクラスは Microsoft.EntityFrameworkCore.DbContext クラスから派生させて作成します。
Models
フォルダーを右クリックして、[追加]>[クラス] の順に選択します。 クラスに「TodoContext」という名前を付け、 [追加] をクリックします。
次のコードを入力します。
using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; public class TodoContext : DbContext { public TodoContext(DbContextOptions<TodoContext> options) : base(options) { } public DbSet<TodoItem> TodoItems { get; set; } = null!; }
データベース コンテキストの登録
ASP.NET Core で、サービス (DB コンテキストなど) を依存関係の挿入 (DI)コンテナーに登録する必要があります。 コンテナーは、コントローラーにサービスを提供します。
次の強調表示されているコードを使用して、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.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
上記のコードでは次の操作が行われます。
using
ディレクティブを追加します。- DI コンテナーにデータベース コンテキストを追加します。
- データベース コンテキストがメモリ内データベースを使用することを指定します。
コントローラーのスキャフォールディング
Controllers
フォルダーを右クリックしします。[追加]>New Scaffolded Item を選択します。
[Entity Framework を使用したアクションがある API コントローラー] を選択してから、 [追加] を選択します。
[Entity Framework を使用したアクションがある API コントローラー] ダイアログで次を実行します。
- モデル クラスで [TodoItem (TodoApi.Models)] を選択します。
- データ コンテキスト クラスで [TodoContext (TodoApi.Models)] を選択します。
- [追加] を選びます。
スキャフォールディング操作が失敗した場合は、 [追加] を選択して、もう一度スキャフォールディングを試行します。
生成されたコードでは次の操作が行われます。
- クラスを
[ApiController]
属性でマークします。 この属性は、コントローラーが Web API 要求に応答することを示します。 属性によって有効化される特定の動作については、「ASP.NET Core を使って Web API を作成する」を参照してください。 - DI を使用して、データベース コンテキスト (
TodoContext
) をコントローラーに挿入します。 データベース コンテキストは、コントローラーの各 CRUD メソッドで使用されます。
ASP.NET Core テンプレートの対象は次のとおりです。
- ビューを含むコントローラーには、ルート テンプレートの
[action]
が含まれます。 - API コントローラーには、ルート テンプレートの
[action]
が含まれません。
[action]
トークンがルート テンプレートにない場合、アクション名 (メソッド名) はエンドポイントに含まれません。 つまり、アクションの関連付けられたメソッド名は一致するルートでは使用されません。
PostTodoItem 作成メソッドの更新
nameof 演算子を使用するために、PostTodoItem
で return ステートメントを更新します。
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
[HttpPost]
属性が示すように、上記のコードは HTTP POST
メソッドです。 このメソッドは、HTTP 要求の本文から TodoItem
の値を取得します。
詳細については、「Http[Verb] 属性を使用する属性ルーティング」を参照してください。
CreatedAtAction メソッド:
- 成功すると、HTTP 201 状態コードが返されます。
HTTP 201
は、サーバーに新しいリソースを作成するHTTP POST
メソッドに対する標準の応答です。 - 応答に Location ヘッダーが追加されます。
Location
ヘッダーでは、新しく作成された To Do アイテムの URI が指定されます。 詳細については、「10.2.2 201 Created」を参照してください。 GetTodoItem
アクションを参照してLocation
ヘッダーの URI を作成します。 C# のnameof
キーワードを使って、CreatedAtAction
呼び出しでアクション名をハードコーディングすることを回避しています。
PostTodoItem のテスト
Ctrl キーを押しながら F5 キーを押して、アプリを実行します。
Swagger ブラウザー ウィンドウで、[POST /api/TodoItems] を選択し、次に [試してみる] を選択します。
要求本文の入力ウィンドウで、JSON を更新します。 たとえば、 にします。
{ "name": "walk dog", "isComplete": true }
[実行] を選択します
場所ヘッダー URI のテスト
上記の POST では、Swagger UI の [応答ヘッダー] に場所ヘッダーが表示されています。 たとえば、「 location: https://localhost:7260/api/TodoItems/1
」のように入力します。 場所ヘッダーには、作成されたリソースへの URI が表示されます。
場所ヘッダーをテストするには:
Swagger ブラウザー ウィンドウで、[GET /api/TodoItems/{id}] を選択し、次に [試してみる] を選択します。
id
入力ボックスに1
を入力し、[実行] を選択します。
GET メソッドの確認
2 つの GET エンドポイントが実装されます。
GET /api/todoitems
GET /api/todoitems/{id}
前のセクションでは、/api/todoitems/{id}
ルートの例を示しました。
POST の指示に従って別の todo 項目を追加し、Swagger を使用して /api/todoitems
ルートをテストします。
このアプリではメモリ内データベースが使用されます。 アプリが停止して開始された場合、上記の GET 要求はどのようなデータも返しません。 データが返されない場合は、アプリにデータを POST します。
ルーティングと URL パス
[HttpGet]
属性は、HTTP GET
要求に応答するメソッドを表します。 各メソッドの URL パスは次のように構成されます。
コントローラーの
Route
属性でテンプレート文字列を使用します。[Route("api/[controller]")] [ApiController] public class TodoItemsController : ControllerBase
[controller]
をコントローラーの名前 (慣例では "Controller" サフィックスを除くコントローラー クラス名) に置き換えます。 このサンプルでは、コントローラー クラス名は TodoItemsController なので、コントローラー名は "TodoItems" です。 ASP.NET Core のルーティングでは、大文字と小文字が区別されません。[HttpGet]
属性にルート テンプレート (たとえば、[HttpGet("products")]
) がある場合は、それをパスに追加します。 このサンプルではテンプレートを使用しません。 詳細については、「Http[Verb] 属性を使用する属性ルーティング」を参照してください。
次の GetTodoItem
メソッドで、"{id}"
は To Do アイテムの一意識別子に使用するプレースホルダーの変数です。 GetTodoItem
が呼び出されると、その id
パラメーター内のメソッドに URL の "{id}"
の値が指定されます。
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
戻り値
GetTodoItems
メソッドと GetTodoItem
メソッドの戻り値の型は、ActionResult<T> 型です。 ASP.NET Core は自動的にオブジェクトを JSON にシリアル化して、応答メッセージの本文に JSON を書き込みます。 ハンドルされない例外がないと仮定すると、この戻り値の型の応答コードは 200 OK です。 ハンドルされない例外は 5xx エラーに変換されます。
ActionResult
戻り値の型は、幅広い範囲の HTTP 状態コードを表すことができます。 たとえば、GetTodoItem
は、次の 2 つの異なる状態値を返す可能性があります。
- 要求された ID に一致するアイテムがない場合、このメソッドにより 404 ステータス NotFound エラー コードが返されます。
- それ以外の場合、メソッドは JSON 応答本文で 200 を返します。 戻り値が
item
の場合、HTTP 200
応答が返されます。
PutTodoItem メソッド
PutTodoItem
メソッドを検証します。
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
if (id != todoItem.Id)
{
return BadRequest();
}
_context.Entry(todoItem).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
PutTodoItem
は PostTodoItem
と似ていますが、HTTP PUT
を使用します。 応答は 204 (No Content) となります。 HTTP 仕様に従って、PUT
要求では、変更だけでなく、更新されたエンティティ全体を送信するようクライアントに求めます。 部分的な更新をサポートするには、HTTP PATCH を使用します。
PutTodoItem メソッドのテスト
このサンプルでは、アプリを起動するたびに開始することが必要なメモリ内データベースが使われています。 PUT 呼び出しを実行する前に、データベース内にアイテムが存在している必要があります。 GET を呼び出して、PUT 呼び出しを実行する前にデータベース内にアイテムが確実に存在していることを確認します。
Swagger UI を使用して、PUT ボタンを使用して Id = 1 の TodoItem
を更新し、その名前を "feed fish"
に設定します。 応答は HTTP 204 No Content
であることに注意してください。
DeleteTodoItem メソッド
DeleteTodoItem
メソッドを検証します。
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
DeleteTodoItem メソッドのテスト
Swagger UI を使用して、Id = 1 の TodoItem
を削除します。 応答は HTTP 204 No Content
であることに注意してください。
他のツールでテストする
Web API のテストには、他にも使用できるツールが多数あります。次に例を示します。
- Visual Studio Endpoints Explorer と .http ファイル
- http-repl
- curl。 Swagger は
curl
を使い、送信されたcurl
コマンドを表示します。 - Fiddler
詳細については、次を参照してください。
過剰な投稿を防止する
現在、サンプル アプリでは TodoItem
オブジェクト全体が公開されています。 通常、運用環境のアプリでは、モデルのサブセットを使用して入力されるデータおよび返されるデータが制限されています。 その背景には複数の理由があり、セキュリティは主なものです。 モデルのサブセットは、通常、データ転送オブジェクト (DTO)、入力モデル、またはビュー モデルと呼ばれます。 このチュートリアルでは DTO を使用しています。
DTO は次の目的で使用できます。
- 過剰な投稿を防止する。
- クライアントが表示しないことになっているプロパティを非表示にする。
- ペイロード サイズを減らすために、いくつかのプロパティを省略する。
- 入れ子になったオブジェクトを含むオブジェクト グラフをフラット化する。 フラット化されたオブジェクト グラフは、クライアントにとってより便利になる可能性があります。
DTO のアプローチを実演するために、TodoItem
クラスを更新して、シークレット フィールドを含めます。
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
}
シークレット フィールドは、このアプリでは非表示にする必要がありますが、管理アプリの場合は公開することを選択できます。
シークレット フィールドを投稿および取得できることを確認します。
次のように DTO モデルを作成します。
namespace TodoApi.Models;
public class TodoItemDTO
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
TodoItemDTO
を使用するように TodoItemsController
を更新します。
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
{
_context = context;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
}
// GET: api/TodoItems/5
// <snippet_GetByID>
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return ItemToDTO(todoItem);
}
// </snippet_GetByID>
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Update>
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
{
if (id != todoDTO.Id)
{
return BadRequest();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
todoItem.Name = todoDTO.Name;
todoItem.IsComplete = todoDTO.IsComplete;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
return NoContent();
}
// </snippet_Update>
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Create>
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoDTO.IsComplete,
Name = todoDTO.Name
};
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
}
// </snippet_Create>
// 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();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
private bool TodoItemExists(long id)
{
return _context.TodoItems.Any(e => e.Id == id);
}
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
シークレット フィールドを投稿または取得できないことを確認します。
JavaScript を使用した Web API の呼び出し
「チュートリアル:JavaScript を使用して ASP.NET Core Web API を呼び出す」を参照してください。
Web API ビデオ シリーズ
ビデオ: 「ビギナーズ シリーズ: Web API」を参照してください。
信頼性の高い Web アプリ パターン
ゼロから、または既存のアプリをリファクタリングして、信頼性とパフォーマンスが高く、テスト可能で、コスト効率が高く、スケーラブルな最新の ASP.NET Core アプリを作成する方法のガイダンスについては、"信頼性の高い .NET の Web アプリ パターン" に関する YouTube のビデオと記事をご覧ください。
Web API に認証サポートを追加
ASP.NET Core Identity では、ASP.NET Core Web アプリにユーザー インターフェイス (UI) ログイン機能が追加されます。 Web API と SPA をセキュリティで保護するには、次のいずれかを使用します。
- Microsoft Entra ID
- Azure Active Directory B2C (Azure AD B2C)
- Duende Identity Server
Duende Identity Server は、ASP.NET Core 用の OpenID Connect および OAuth 2.0 フレームワークです。 Duende Identity Server により、次のセキュリティ機能が有効になります。
- サービスとしての認証 (AaaS)
- 複数のアプリケーションの種類でのシングル サインオン/オフ (SSO)
- API のアクセス制御
- Federation Gateway
重要
Duende Software により、Duende Identity Server を実稼働で使用することのライセンス料の支払いが求められる場合があります。 詳細については、「ASP.NET Core 5.0 から 6.0 への移行」を参照してください。
詳細については、Duende Identity Server に関するドキュメント (Duende ソフトウェアの Web サイト) を参照してください。
Azure に発行する
Azure へのデプロイについては、「クイックスタート: ASP.NET Web アプリをデプロイする」を参照してください。
その他の技術情報
このチュートリアルのサンプル コードを表示またはダウンロードします。 ダウンロード方法に関するページを参照してください。
詳細については、次のリソースを参照してください。
- ASP.NET Core を使って Web API を作成する
- チュートリアル: ASP.NET Core を使って最小 API を作成する
- Swagger/OpenAPI を使用する ASP.NET Core Web API のドキュメント
- RazorASP.NET Core での Entity Framework Core を使用した Pages - チュートリアル 1/8
- ASP.NET Core でのコントローラー アクションへのルーティング
- ASP.NET Core Web API のコントローラー アクションの戻り値の型
- Azure App Service に ASP.NET Core アプリを展開する
- ASP.NET Core のホストと展開
- ASP.NET Core で Web API を作成する
ASP.NET Core