【ASP.NET Core】Azure Cosmos DBと連携したAPIを作成する

ASP.NET Core

ASP.NET Core Web API(C#)と、NoSQLデータベースサービスであるAzure Cosmos DBを使用して、シンプルなAPIを作成する方法を解説します。

もしASP.NET Core Web APIを触ったことがない場合は、以下の記事を参考にチュートリアルをやってみることをおすすめします。

本記事は以下の公式チュートリアルをベースに作成しています。(チュートリアルではASP.NET Core MVCを使用)

Azure Cosmos DB を使用した ASP.NET Core MVC Web アプリのチュートリアル
Azure Cosmos DB を使用して MVC Web アプリケーションを作成するための ASP.NET Core MVC チュートリアル。 JSON を格納し、Azure App Service 上でホストされている ToDo アプリからデータにアクセスします - ASP.NET Core MVC チュートリアル...
スポンサーリンク

環境

  • Windows 10
  • 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 2022を開き、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にCsomos 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は下記ページからダウンロードできます。

Download Postman | Get Started for Free
Try Postman for free! Join 20 million developers who rely on Postman, the collaboration platform for API development. Create better APIs—faster.

データの登録

まずは新たな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も試してみてください。

おすすめ書籍

著:増田 智明
¥3,366 (2022/11/30 23:31時点 | Amazon調べ)
\楽天ポイント5倍セール!/
楽天市場
\ポイント5%還元!/
Yahooショッピング

コメント

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