ASP.NET Core Web API(C#)とAzureのNoSQLサービスであるAzure Cosmos DBを使って、シンプルなAPIを作成する方法を解説します。
本記事は以下の公式チュートリアルをベースに作成しています。
環境
- Visual Studio 2022
- .NET 6
前提
- Azureアカウントが作成済みであること
Azure Cosmos DBアカウントの作成
まずCosmos DB(Freeプラン)のアカウント作成方法について説明します。
既に作成済みの場合は飛ばしてください。
Azureポータルを開き、上部の検索窓に「cosmos」と入力すると、検索結果に「Azure Cosmos DB」が表示されるのでクリックします。
左上の「作成」をクリックします。
複数のAPIが表示されますが、今回は「コア (SQL) – 推奨」を選択します。
赤枠部分を入力後、「レビュー + 作成」をクリックします。
※主な設定項目の説明
- サブスクリプション:使用したいサブスクリプションを選択します。
- リソースグループ:任意の名前で新規作成します。作成済みのリソースグループがある場合はそれを選択してもOKです。
- アカウント名:任意の名前を設定します。
- 場所:日本国内の場合は一般的に「Japan East」か「Japan West」を選択します。
- Freeレベル割引の適用:一定の範囲まで無料で使えるので必ず「適用」(デフォルト)にしましょう。
※Freeレベルは1つのAzure サブスクリプションで1つまでしか適用できないので注意
その他の設定についてはデフォルトのままで大丈夫です。
問題がなければ「作成」をクリックします。完了するまで数分待ちましょう。
作成が完了したら、「リソースに移動」をクリックします。
メニューの「キー」からURIとプライマリキーを確認します。これらは後に使用します。
ASP.NET Core Web APIの作成
それではAPIを作成していきましょう。今回はTodoアイテムを管理するシンプルなAPIを作成します。
プロジェクトの作成とパッケージのインストール
Visual Studioを開き、ASP.NET Core Web APIプロジェクトを作成します。
今回は「CosmosTodoApi」という名前で作成していますが、プロジェクト名はなんでもOKです。
Swaggerは使用しないため「Open API サポートを有効にする」のチェックは外しましょう。
プロジェクトが作成できたら、Cosmos DBとの連携に必要なパッケージをインストールします。
プロジェクト名を右クリック→「Nuget パッケージの管理」からNuget パッケージマネージャーを開き、「Microsoft.Azure.Cosmos」をインストールします。
モデルの作成
まずTodoItemモデルを作成します。
プロジェクトの直下に「Models」というフォルダを作成し、その中に「TodoItem.cs」を追加してください。
using Newtonsoft.Json;
namespace CosmosTodoApi.Models
{
public class TodoItem
{
[JsonProperty("id")]
public string Id { get; set; } = string.Empty;
[JsonProperty("name")]
public string? Name { get; set; }
[JsonProperty("isComplete")]
public bool IsComplete { get; set; }
}
}
JsonProperty属性を使うと、JSONオブジェクト変換時の名称を指定することができます。
※2022年11月時点では、Cosmos DBのSDKがNewtonsoft.Jsonに依存しているため、System.Text.Json名前空間の属性を使って名称を変換しようとするとエラーになります。
Azure Cosmos DBを操作するサービスクラスの作成
Azure Cosmos DBに対するCRUD操作のロジックをまとめたサービスクラスを作成します。
具体的なロジックを定義する「CosmosDbService」と、それらをカプセル化したインターフェース「ICosmosDbService」を作成していきます。
まずプロジェクトの直下に「Services」というフォルダを作成し、その中に「ICosmosDbService」を追加してください。
using CosmosTodoApi.Models;
namespace CosmosTodoApi.Services
{
public interface ICosmosDbService
{
Task<IEnumerable<TodoItem>> GetItemsAsync(string query);
Task<TodoItem?> GetItemAsync(string id);
Task AddItemAsync(TodoItem item);
Task UpdateItemAsync(string id, TodoItem item);
Task DeleteItemAsync(string id);
}
}
同様に「CosmosDbService」という実装クラスを追加します。各処理の中身がわかるように簡単にコメントを付与しています。
using CosmosTodoApi.Models;
using Microsoft.Azure.Cosmos;
namespace CosmosTodoApi.Services
{
public class CosmosDbService : ICosmosDbService
{
private readonly Container _container;
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="cosmosClient">Cosmosクライアント</param>
/// <param name="databaseName">データベース名</param>
/// <param name="containerName">コンテナ名</param>
public CosmosDbService(CosmosClient cosmosClient, string databaseName, string containerName)
{
_container = cosmosClient.GetContainer(databaseName, containerName);
}
/// <summary>
/// クエリに一致するTodoアイテムを取得する
/// </summary>
/// <param name="queryString">SQLクエリ</param>
/// <returns></returns>
public async Task<IEnumerable<TodoItem>> GetItemsAsync(string queryString)
{
var query = _container.GetItemQueryIterator<TodoItem>(new QueryDefinition(queryString));
var results = new List<TodoItem>();
while (query.HasMoreResults)
{
var response = await query.ReadNextAsync();
results.AddRange(response.ToList());
}
return results;
}
/// <summary>
/// Todoアイテムを1件取得する
/// </summary>
/// <param name="id">Id</param>
/// <returns></returns>
public async Task<TodoItem?> GetItemAsync(string id)
{
try
{
ItemResponse<TodoItem> response = await _container.ReadItemAsync<TodoItem>(id, new PartitionKey(id));
return response.Resource;
}
// 404 Not Foundの場合はnullを返す
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return null;
}
}
/// <summary>
/// Todoアイテムを登録する
/// </summary>
/// <param name="item">Todoアイテム</param>
/// <returns></returns>
public async Task AddItemAsync(TodoItem item)
{
await _container.CreateItemAsync<TodoItem>(item, new PartitionKey(item.Id));
}
/// <summary>
/// Todoアイテムを更新する
/// </summary>
/// <param name="id">Id</param>
/// <param name="item">Todoアイテム</param>
/// <returns></returns>
public async Task UpdateItemAsync(string id, TodoItem item)
{
await _container.UpsertItemAsync<TodoItem>(item, new PartitionKey(id));
}
/// <summary>
/// Todoアイテムを削除する
/// </summary>
/// <param name="id">Id</param>
/// <returns></returns>
public async Task DeleteItemAsync(string id)
{
await _container.DeleteItemAsync<TodoItem>(id, new PartitionKey(id));
}
}
}
設定ファイルの変更
次にProgram.csを変更します。
Cosmos DBに接続するクライアントの初期設定と、クライアントのインスタンスをシングルトンにする(アプリ起動時に一度だけインスタンスを生成する)設定を記載します。
using CosmosTodoApi.Services;
using Microsoft.Azure.Cosmos;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
// Cosmosクライアントの初期化処理
static async Task<CosmosDbService> InitializeCosmosClientInstanceAsync(IConfigurationSection configurationSection)
{
// appsettings.jsonからCosmosDBの設定値を取得
var databaseName = configurationSection["DatabaseName"];
var containerName = configurationSection["ContainerName"];
var account = configurationSection["Account"];
var key = configurationSection["Key"];
// CosmosClientとCosmosDbServiceのインスタンスを生成
var client = new CosmosClient(account, key);
var cosmosDbService = new CosmosDbService(client, databaseName, containerName);
// データベースが存在しない場合は新たに作成する
var database = await client.CreateDatabaseIfNotExistsAsync(databaseName);
// コンテナが存在しない場合は新たに作成する
await database.Database.CreateContainerIfNotExistsAsync(containerName, "/id");
return cosmosDbService;
}
// Cosmosクライアントのシングルトンインスタンスを生成
builder.Services.AddSingleton<ICosmosDbService>(
InitializeCosmosClientInstanceAsync(builder.Configuration.GetSection("CosmosDb")).GetAwaiter().GetResult());
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
appsettings.jsonにCosmos DBに関する情報を追加します。
Azureポータルで確認したURIとプライマリキーをコピペしてください。
{
"Logging": {
"LogLevel": {
"Default": "Information"
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"CosmosDb": {
"Account": "<URI>",
"Key": "<プライマリキー>",
"DatabaseName": "Todo",
"ContainerName": "TodoItem"
}
}
デバッグ実行時の設定も変更します。
Properties/launchSettings.json内のlaunchUrlを以下のように変更してください。
...
"profiles": {
"CosmosTodoApi": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "api/todoitems",
"applicationUrl": "https://localhost:<ポート番号>;http://localhost:<ポート番号>",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
...
APIコントローラーの作成
「Controllers」フォルダ内に「TodoItemsController」という名前の空のAPIコントローラーを追加し、以下のコードを記述します。
using CosmosTodoApi.Models;
using CosmosTodoApi.Services;
using Microsoft.AspNetCore.Mvc;
namespace CosmosTodoApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly ICosmosDbService _cosmosDbService;
public TodoItemsController(ICosmosDbService cosmosDbService)
{
_cosmosDbService = cosmosDbService;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItem>>> Get()
{
var result = await _cosmosDbService.GetItemsAsync("SELECT * FROM c");
return result.ToList();
}
// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(string id)
{
var todoItem = await _cosmosDbService.GetItemAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
// PUT: api/TodoItems/5
[HttpPut]
public async Task<IActionResult> Put(string id, TodoItem item)
{
if (id != item.Id)
{
return BadRequest();
}
await _cosmosDbService.UpdateItemAsync(id, item);
return NoContent();
}
// POST: api/TodoItems
[HttpPost]
public async Task<ActionResult<TodoItem>> Post(TodoItem item)
{
item.Id = Guid.NewGuid().ToString();
await _cosmosDbService.AddItemAsync(item);
return CreatedAtAction("Get", new { id = item.Id }, item);
}
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(string id)
{
var todoItem = await _cosmosDbService.GetItemAsync(id);
if (todoItem == null)
{
return NotFound();
}
await _cosmosDbService.DeleteItemAsync(id);
return NoContent();
}
}
}
以上でAPIの作成は完了です。
動作確認
それでは正しくAPIが動くかどうかを確認してみましょう。
今回はPostmanを使ってAPIにリクエストしてみます。
※Postmanは下記ページからダウンロードできます。
データの登録
まずは新たなTodoアイテムを登録してみたいと思います。アプリをF5でデバッグ実行しておいてください。
URLは「https://localhost:<ポート番号>/api/todoitems」にし、bodyに下記のJSONを設定してPOSTリクエストを送信してみましょう。
※<ポート番号>には、デバッグ実行時にブラウザのアドレスバーに表示されているポート番号を設定してください。
{
"name": "買い物をする",
"isComplete": false
}
※このような画面が表示された場合は「Disable SSL Vertfication」をクリックしてください。
このようなレスポンスが返ってくれば成功です。
Cosmos DB側も見てみましょう。
データエクスプローラーでデータを展開すると、POSTしたデータがJSON形式で保存されていることが確認できます。
データの取得
登録したデータが取得できることを確認するため、api/todoitemsにGETリクエストを送ってみます。
「https://localhost:<ポート番号>/api/todoitems」宛にGETリクエストを送信してみてください。
このようなレスポンスが返ってくれば成功です。
以上でAPIが正しく動作していることが確認できました。
興味のある方はPUTやDELETEのAPI実行も試してみてください。
参考書籍