ASP.NET Core と MongoDB で Web API を作成する

Note

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

作成者: Pratik Khandelwal および Scott Addie

このチュートリアルでは、MongoDB NoSQL データベース上で Create、Read、Update、Delete の各操作 (CRUD) を実行する Web API を作成します。

このチュートリアルでは、次の作業を行う方法について説明します。

  • MongoDB を構成する
  • MongoDB データベースを作成する
  • MongoDB のコレクションとスキーマを定義する
  • Web API から MongoDB CRUD 操作を実行する
  • JSON シリアル化をカスタマイズする

必須コンポーネント

MongoDB を構成する

開発用コンピューター (Windows/Linux/macOS) 上のどこからでも MongoDB および Mongo DB Shell のアクセスを有効にします。

  1. MongoDB Shell をダウンロードし、インストールします。

    • macOS/Linux: MongoDB Shell の抽出先となるディレクトリを選択します。 mongosh の作成されたパスを PATH 環境変数に追加します。
    • Windows: MongoDB Shell (mongosh.exe) は C:\Users<user>\AppData\Local\Programs\mongosh にインストールされます。 mongosh.exe の作成されたパスを PATH 環境変数に追加します。
  2. MongoDB をダウンロードし、インストールします。

    • macOS/Linux: MongoDB がインストールされたディレクトリを確認します。通常は /usr/local/mongodb です。 mongodb の作成されたパスを PATH 環境変数に追加します。
    • Windows: 既定では、MongoDB は C:\Program Files\MongoDB にインストールされます。 C:\Program Files\MongoDB\Server\<version_number>\binPATH 環境変数に追加します。
  3. データ ストレージ ディレクトリの選択: データを格納するためのディレクトリを開発用コンピューター上で選択します。 存在しない場合はディレクトリを作成します。 MongoDB Shell では新しいディレクトリは作成されません。

    • macOS/Linux: /usr/local/var/mongodb など。
    • Windows: C:\\BooksData など。
  4. OS のコマンド シェル (MongoDB Shell ではなく) で、次のコマンドを使って、既定のポート 27017 で MongoDB に接続します。 <data_directory_path> を、前の手順で選択したディレクトリに置き換えます。

    mongod --dbpath <data_directory_path>
    

次の手順では、以前にインストールした MongoDB シェルを使用して、データベースを作成し、コレクションを作成し、ドキュメントを保存します。 MongoDB シェル コマンドの詳細については、「mongosh」を参照してください。

  1. mongosh.exe を起動して、MongoDB コマンド シェル インスタンスを開きます。

  2. コマンド シェルで、次のコマンドを実行し、既定のテスト データベースに接続します。

    mongosh
    
  3. 次のコマンドをコマンド シェルで実行します。

    use BookStore
    

    BookStore という名前のデータベースが、まだ存在しない場合は作成されます。 データベースが存在する場合は、トランザクションのために接続されます。

  4. 次のコマンドを使用して Books コレクションを作成します。

    db.createCollection('Books')
    

    次のような結果が表示されます。

    { "ok" : 1 }
    
  5. 次のコマンドを使用して、Books コレクションのスキーマを定義し、2 つのドキュメントを挿入します。

    db.Books.insertMany([{ "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Name": "Clean Code", "Price": 43.15, "Category": "Computers","Author": "Robert C. Martin" }])
    

    次のような結果が表示されます。

    {
        "acknowledged" : true,
        "insertedIds" : [
            ObjectId("61a6058e6c43f32854e51f51"),
            ObjectId("61a6058e6c43f32854e51f52")
         ]
     }
    

    Note

    前述の結果に示されている ObjectId は、実際のコマンド シェルの表示内容とは一致しません。

  6. 次のコマンドを使用して、データベース内のドキュメントを表示します。

    db.Books.find().pretty()
    

    次のような結果が表示されます。

    {
         "_id" : ObjectId("61a6058e6c43f32854e51f51"),
         "Name" : "Design Patterns",
         "Price" : 54.93,
         "Category" : "Computers",
         "Author" : "Ralph Johnson"
     }
     {
         "_id" : ObjectId("61a6058e6c43f32854e51f52"),
         "Name" : "Clean Code",
         "Price" : 43.15,
         "Category" : "Computers",
         "Author" : "Robert C. Martin"
     }
    

    スキーマによって、自動生成された型が ObjectId_id プロパティが各ドキュメントに追加されます。

ASP.NET Core Web API プロジェクトを作成する

  1. [ファイル]>[新規]>[プロジェクト] の順に移動します。

  2. [ASP.NET Core Web API] プロジェクトの種類を選択し、[次へ] を選択します。

  3. プロジェクトに「BookStoreApi」という名前を付け、[次へ] を選択します。

  4. [.NET 8.0 (長期的なサポート)] フレームワークを選択し、[作成] を選択します。

  5. [パッケージ マネージャー コンソール] ウィンドウで、プロジェクトのルートに移動します。 次のコマンドを実行して、MongoDB 用の .NET ドライバーをインストールします。

    Install-Package MongoDB.Driver
    

モデルにエンティティを追加する

  1. Models ディレクトリをプロジェクトのルートに追加します。

  2. 次のコードを使用して、Book クラスを Book ディレクトリに追加します。

    using MongoDB.Bson;
    using MongoDB.Bson.Serialization.Attributes;
    
    namespace BookStoreApi.Models;
    
    public class Book
    {
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public string? Id { get; set; }
    
        [BsonElement("Name")]
        public string BookName { get; set; } = null!;
    
        public decimal Price { get; set; }
    
        public string Category { get; set; } = null!;
    
        public string Author { get; set; } = null!;
    }
    

    上記のクラスでは、Id プロパティは:

    • 共通言語ランタイム (CLR) オブジェクトを MongoDB コレクションにマッピングするために必須です。
    • このプロパティをドキュメントの主キーとするために、[BsonId] で注釈を付けられています。
    • [BsonRepresentation(BsonType.ObjectId)] 構造体ではなく string 型としてパラメーターを渡すことができるようにするために、[BsonRepresentation(BsonType.ObjectId)] で注釈を付けられています。 Mongo によって string から ObjectId への変換が処理されます。

    BookName プロパティには、[BsonElement] 属性を使用して注釈が付けられます。 属性の値 Name は、MongoDB コレクションでのプロパティ名を表します。

構成モデルを追加する

  1. 次のデータベース構成値を appsettings.json に追加します。

    {
        "BookStoreDatabase": {
            "ConnectionString": "mongodb://localhost:27017",
            "DatabaseName": "BookStore",
            "BooksCollectionName": "Books"
        },
        "Logging": {
            "LogLevel": {
                "Default": "Information",
                "Microsoft.AspNetCore": "Warning"
            }
        },
        "AllowedHosts": "*"
    }
    
  2. 次のコードを使用して、BookStoreDatabaseSettings クラスを BookStoreDatabaseSettings ディレクトリに追加します。

    namespace BookStoreApi.Models;
    
    public class BookStoreDatabaseSettings
    {
        public string ConnectionString { get; set; } = null!;
    
        public string DatabaseName { get; set; } = null!;
    
        public string BooksCollectionName { get; set; } = null!;
    }
    

    前述の BookStoreDatabaseSettings クラスは、 appsettings.json ファイルの BookStoreDatabase プロパティ値を格納するために使用されます。 JSON と C# のプロパティ名には、マッピング処理を簡単にするために同じ名前が付けられています。

  3. 次の強調表示されたコードを Program.cs に追加します。

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    

    上のコードで、appsettings.json ファイルの BookStoreDatabase セクションがバインドされている構成インスタンスは、Dependency Injection (DI) コンテナーに登録されています。 たとえば、BookStoreDatabaseSettings オブジェクトの ConnectionString プロパティには、appsettings.jsonBookStoreDatabase:ConnectionString プロパティが設定されています。

  4. 次のコードを Program.cs の先頭に追加して、BookStoreDatabaseSettings の参照を解決します。

    using BookStoreApi.Models;
    

CRUD 操作のサービスを追加する

  1. Services ディレクトリをプロジェクトのルートに追加します。

  2. 次のコードを使用して、BooksService クラスを BooksService ディレクトリに追加します。

    using BookStoreApi.Models;
    using Microsoft.Extensions.Options;
    using MongoDB.Driver;
    
    namespace BookStoreApi.Services;
    
    public class BooksService
    {
        private readonly IMongoCollection<Book> _booksCollection;
    
        public BooksService(
            IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
        {
            var mongoClient = new MongoClient(
                bookStoreDatabaseSettings.Value.ConnectionString);
    
            var mongoDatabase = mongoClient.GetDatabase(
                bookStoreDatabaseSettings.Value.DatabaseName);
    
            _booksCollection = mongoDatabase.GetCollection<Book>(
                bookStoreDatabaseSettings.Value.BooksCollectionName);
        }
    
        public async Task<List<Book>> GetAsync() =>
            await _booksCollection.Find(_ => true).ToListAsync();
    
        public async Task<Book?> GetAsync(string id) =>
            await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync();
    
        public async Task CreateAsync(Book newBook) =>
            await _booksCollection.InsertOneAsync(newBook);
    
        public async Task UpdateAsync(string id, Book updatedBook) =>
            await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook);
    
        public async Task RemoveAsync(string id) =>
            await _booksCollection.DeleteOneAsync(x => x.Id == id);
    }
    

    前述のコードでは、BookStoreDatabaseSettings インスタンスがコンストラクターの挿入によって DI から取得されます。 この手法で、「appsettings.json」セクションで追加した appsettings.json 構成値にアクセスできます。

  3. 次の強調表示されたコードを Program.cs に追加します。

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    
    builder.Services.AddSingleton<BooksService>();
    

    前述のコードでは、消費クラスへのコンストラクターの挿入をサポートする BooksService クラスが DI に登録されています。 BooksServiceMongoClient に直接依存しているため、シングルトン サービスの有効期間が最も適切です。 公式の Mongo Client 再利用ガイドラインに従い、シングルトン サービスの有効期間を使用して DI に MongoClient を登録する必要があります。

  4. 次のコードを Program.cs の先頭に追加して、BooksService の参照を解決します。

    using BookStoreApi.Services;
    

BooksService クラスは次の MongoDB.Driver メンバーを使用して、データベースに対する CRUD 操作を実行します。

  • MongoClient: データベース操作を実行するためにサーバー インスタンスを読み取ります。 このクラスのコンストラクターは、MongoDB 接続文字列内で提供されます。

    public BooksService(
        IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
    {
        var mongoClient = new MongoClient(
            bookStoreDatabaseSettings.Value.ConnectionString);
    
        var mongoDatabase = mongoClient.GetDatabase(
            bookStoreDatabaseSettings.Value.DatabaseName);
    
        _booksCollection = mongoDatabase.GetCollection<Book>(
            bookStoreDatabaseSettings.Value.BooksCollectionName);
    }
    
  • IMongoDatabase: 操作を実行する Mongo データベースを表します。 このチュートリアルでは、インターフェイスで汎用の GetCollection<TDocument>(collection) メソッドを使って、特定のコレクション内のデータにアクセスします。 このメソッドが呼び出された後に、CRUD 操作をこのコレクションに対して実行します。 GetCollection<TDocument>(collection) メソッドの呼び出しの内容は次のとおりです。

    • collection はコレクション名です。
    • TDocument はコレクションに格納されている CLR オブジェクト型です。

GetCollection<TDocument>(collection) から、コレクションを表す GetCollection<TDocument>(collection) オブジェクトが返されます。 このチュートリアルでは、コレクションに対して次のメソッドを呼び出します。

  • DeleteOneAsync: 指定された検索基準と一致する 1 つのドキュメントを削除します。
  • Find<TDocument>: 指定された検索基準と一致するコレクション内のすべてのドキュメントを返します。
  • InsertOneAsync: 指定されたオブジェクトを新しいドキュメントとしてコレクションに挿入します。
  • ReplaceOneAsync: 指定された検索基準と一致する 1 つのドキュメントを、指定されたオブジェクトで置き換えます。

コントローラーの追加

次のコードを使用して、BooksController クラスを BooksController ディレクトリに追加します。

using BookStoreApi.Models;
using BookStoreApi.Services;
using Microsoft.AspNetCore.Mvc;

namespace BookStoreApi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
    private readonly BooksService _booksService;

    public BooksController(BooksService booksService) =>
        _booksService = booksService;

    [HttpGet]
    public async Task<List<Book>> Get() =>
        await _booksService.GetAsync();

    [HttpGet("{id:length(24)}")]
    public async Task<ActionResult<Book>> Get(string id)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        return book;
    }

    [HttpPost]
    public async Task<IActionResult> Post(Book newBook)
    {
        await _booksService.CreateAsync(newBook);

        return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook);
    }

    [HttpPut("{id:length(24)}")]
    public async Task<IActionResult> Update(string id, Book updatedBook)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        updatedBook.Id = book.Id;

        await _booksService.UpdateAsync(id, updatedBook);

        return NoContent();
    }

    [HttpDelete("{id:length(24)}")]
    public async Task<IActionResult> Delete(string id)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        await _booksService.RemoveAsync(id);

        return NoContent();
    }
}

上記の Web API コントローラー:

  • BooksService クラスを使用して CRUD 操作を実行します。
  • GET、POST、PUT、DELETE HTTP 要求をサポートするアクション メソッドが含まれます。
  • Create アクション メソッドで CreatedAtAction を呼び出して、CreatedAtAction 応答を返します。 状態コード 201 は、サーバーに新しいリソースを作成する HTTP POST メソッドに対する標準の応答です。 CreatedAtAction によって、応答に Location ヘッダーも追加されます。 Location ヘッダーでは、新しく作成されたブックの URI を指定します。

Web API をテストする

  1. アプリケーションをビルドし、実行します。

  2. https://localhost:<port>/api/books に移動して、コントローラーのパラメーターなしの Get アクション メソッドをテストします。ここで、<port> は、自動的に割り当てられるアプリのポート番号です。[試してみる][取得] の順に選択します。 次のような JSON 応答が表示されます。

    [
      {
        "id": "61a6058e6c43f32854e51f51",
        "bookName": "Design Patterns",
        "price": 54.93,
        "category": "Computers",
        "author": "Ralph Johnson"
      },
      {
        "id": "61a6058e6c43f32854e51f52",
        "bookName": "Clean Code",
        "price": 43.15,
        "category": "Computers",
        "author": "Robert C. Martin"
      }
    ]
    
  3. https://localhost:<port>/api/books/{id here} に移動して、コントローラーのオーバーロードされた Get アクション メソッドをテストします。 次のような JSON 応答が表示されます。

    {
      "id": "61a6058e6c43f32854e51f52",
      "bookName": "Clean Code",
      "price": 43.15,
      "category": "Computers",
      "author": "Robert C. Martin"
    }
    

JSON シリアル化オプションを構成する

Web API をテストする」セクションで返されている JSON 応答について、変更すべき 2 つの詳細があります。

  • プロパティ名の既定の camel 形式は、CLR オブジェクトのプロパティ名の Pascal 形式と一致するように変更する必要があります
  • bookName プロパティは Name として返される必要があります。

上記の要件を満たすには、次の変更を行います。

  1. Program.cs で、次の強調表示されたコードを AddControllers メソッド呼び出しにチェーンします。

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    
    builder.Services.AddSingleton<BooksService>();
    
    builder.Services.AddControllers()
        .AddJsonOptions(
            options => options.JsonSerializerOptions.PropertyNamingPolicy = null);
    

    上記の変更により、Web API のシリアル化された JSON 応答内のプロパティ名は、CLR のオブジェクトの種類において対応するプロパティ名と一致しています。 たとえば、Book クラスの Author プロパティは、author ではなく Author としてシリアル化されます。

  2. Models/Book.cs では、[JsonPropertyName] 属性を使用して BookName プロパティに注釈を付けます。

    [BsonElement("Name")]
    [JsonPropertyName("Name")]
    public string BookName { get; set; } = null!;
    

    [JsonPropertyName] 属性の Name 値は、Web API のシリアル化された JSON 応答内のプロパティ名を表します。

  3. 次のコードを Models/Book.cs の先頭に追加して、[JsonProperty] 属性の参照を解決します。

    using System.Text.Json.Serialization;
    
  4. Web API をテストする」セクションで定義されている手順を繰り返します。 JSON プロパティ名の違いに注意してください。

Web API に認証サポートを追加

ASP.NET Core Identity では、ASP.NET Core Web アプリにユーザー インターフェイス (UI) ログイン機能が追加されます。 Web API と SPA をセキュリティで保護するには、次のいずれかを使用します。

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 サイト) を参照してください。

その他のリソース

このチュートリアルでは、MongoDB NoSQL データベース上で Create、Read、Update、Delete の各操作 (CRUD) を実行する Web API を作成します。

このチュートリアルでは、次の作業を行う方法について説明します。

  • MongoDB を構成する
  • MongoDB データベースを作成する
  • MongoDB のコレクションとスキーマを定義する
  • Web API から MongoDB CRUD 操作を実行する
  • JSON シリアル化をカスタマイズする

必須コンポーネント

MongoDB を構成する

開発用コンピューター上のどこからでも MongoDB および Mongo DB Shell のアクセスを有効にします。

  1. Windows の場合、既定では MongoDB が C:\Program Files\MongoDB にインストールされます。 C:\Program Files\MongoDB\Server\<version_number>\binPATH 環境変数に追加します。

  2. MongoDB Shell をダウンロードし、それを抽出するディレクトリを選択します。 mongosh.exe の作成されたパスを PATH 環境変数に追加します。

  3. データを格納するための開発用コンピューター上のディレクトリを選択します。 たとえば、Windows では C:\BooksData です。 存在しない場合はディレクトリを作成します。 mongo シェルでは新しいディレクトリは作成されません。

  4. OS のコマンド シェル (MongoDB Shell ではなく) で、次のコマンドを使って、既定のポート 27017 で MongoDB に接続します。 <data_directory_path> を、前の手順で選択したディレクトリに置き換えます。

    mongod --dbpath <data_directory_path>
    

次の手順では、以前にインストールした MongoDB シェルを使用して、データベースを作成し、コレクションを作成し、ドキュメントを保存します。 MongoDB シェル コマンドの詳細については、「mongosh」を参照してください。

  1. mongosh.exe を起動して、MongoDB コマンド シェル インスタンスを開きます。

  2. コマンド シェルで、次のコマンドを実行して、既定のテスト データベースに接続します。

    mongosh
    
  3. 次のコマンドをコマンド シェルで実行します。

    use BookStore
    

    BookStore という名前のデータベースが、まだ存在しない場合は作成されます。 データベースが存在する場合は、トランザクションのために接続されます。

  4. 次のコマンドを使用して Books コレクションを作成します。

    db.createCollection('Books')
    

    次のような結果が表示されます。

    { "ok" : 1 }
    
  5. 次のコマンドを使用して、Books コレクションのスキーマを定義し、2 つのドキュメントを挿入します。

    db.Books.insertMany([{ "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Name": "Clean Code", "Price": 43.15, "Category": "Computers","Author": "Robert C. Martin" }])
    

    次のような結果が表示されます。

    {
        "acknowledged" : true,
        "insertedIds" : [
            ObjectId("61a6058e6c43f32854e51f51"),
            ObjectId("61a6058e6c43f32854e51f52")
         ]
     }
    

    Note

    前述の結果に示されている ObjectId は、実際のコマンド シェルの表示内容とは一致しません。

  6. 次のコマンドを使用して、データベース内のドキュメントを表示します。

    db.Books.find().pretty()
    

    次のような結果が表示されます。

    {
         "_id" : ObjectId("61a6058e6c43f32854e51f51"),
         "Name" : "Design Patterns",
         "Price" : 54.93,
         "Category" : "Computers",
         "Author" : "Ralph Johnson"
     }
     {
         "_id" : ObjectId("61a6058e6c43f32854e51f52"),
         "Name" : "Clean Code",
         "Price" : 43.15,
         "Category" : "Computers",
         "Author" : "Robert C. Martin"
     }
    

    スキーマによって、自動生成された型が ObjectId_id プロパティが各ドキュメントに追加されます。

ASP.NET Core Web API プロジェクトを作成する

  1. [ファイル]>[新規]>[プロジェクト] の順に移動します。

  2. [ASP.NET Core Web API] プロジェクトの種類を選択し、[次へ] を選択します。

  3. プロジェクトに「BookStoreApi」という名前を付け、[次へ] を選択します。

  4. [.NET 7.0 (Standard Term Support)] (.NET 7.0 (標準期間サポート)) フレームワークを選択し、[作成] を選択します。

  5. [ツール] メニューで、[NuGet パッケージ マネージャー]>[パッケージ マネージャー コンソール] の順に選択します。

  6. [パッケージ マネージャー コンソール] ウィンドウで、プロジェクトのルートに移動します。 次のコマンドを実行して、MongoDB 用の .NET ドライバーをインストールします。

    Install-Package MongoDB.Driver
    

モデルにエンティティを追加する

  1. Models ディレクトリをプロジェクトのルートに追加します。

  2. 次のコードを使用して、Book クラスを Book ディレクトリに追加します。

    using MongoDB.Bson;
    using MongoDB.Bson.Serialization.Attributes;
    
    namespace BookStoreApi.Models;
    
    public class Book
    {
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public string? Id { get; set; }
    
        [BsonElement("Name")]
        public string BookName { get; set; } = null!;
    
        public decimal Price { get; set; }
    
        public string Category { get; set; } = null!;
    
        public string Author { get; set; } = null!;
    }
    

    上記のクラスでは、Id プロパティは:

    • 共通言語ランタイム (CLR) オブジェクトを MongoDB コレクションにマッピングするために必須です。
    • このプロパティをドキュメントの主キーとするために、[BsonId] で注釈を付けられています。
    • [BsonRepresentation(BsonType.ObjectId)] 構造体ではなく string 型としてパラメーターを渡すことができるようにするために、[BsonRepresentation(BsonType.ObjectId)] で注釈を付けられています。 Mongo によって string から ObjectId への変換が処理されます。

    BookName プロパティには、[BsonElement] 属性を使用して注釈が付けられます。 属性の値 Name は、MongoDB コレクションでのプロパティ名を表します。

構成モデルを追加する

  1. 次のデータベース構成値を appsettings.json に追加します。

    {
      "BookStoreDatabase": {
        "ConnectionString": "mongodb://localhost:27017",
        "DatabaseName": "BookStore",
        "BooksCollectionName": "Books"
      },
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*"
    }
    
  2. 次のコードを使用して、BookStoreDatabaseSettings クラスを BookStoreDatabaseSettings ディレクトリに追加します。

    namespace BookStoreApi.Models;
    
    public class BookStoreDatabaseSettings
    {
        public string ConnectionString { get; set; } = null!;
    
        public string DatabaseName { get; set; } = null!;
    
        public string BooksCollectionName { get; set; } = null!;
    }
    

    前述の BookStoreDatabaseSettings クラスは、 appsettings.json ファイルの BookStoreDatabase プロパティ値を格納するために使用されます。 JSON と C# のプロパティ名には、マッピング処理を簡単にするために同じ名前が付けられています。

  3. 次の強調表示されたコードを Program.cs に追加します。

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    

    上のコードで、appsettings.json ファイルの BookStoreDatabase セクションがバインドされている構成インスタンスは、Dependency Injection (DI) コンテナーに登録されています。 たとえば、BookStoreDatabaseSettings オブジェクトの ConnectionString プロパティには、appsettings.jsonBookStoreDatabase:ConnectionString プロパティが設定されています。

  4. 次のコードを Program.cs の先頭に追加して、BookStoreDatabaseSettings の参照を解決します。

    using BookStoreApi.Models;
    

CRUD 操作のサービスを追加する

  1. Services ディレクトリをプロジェクトのルートに追加します。

  2. 次のコードを使用して、BooksService クラスを BooksService ディレクトリに追加します。

    using BookStoreApi.Models;
    using Microsoft.Extensions.Options;
    using MongoDB.Driver;
    
    namespace BookStoreApi.Services;
    
    public class BooksService
    {
        private readonly IMongoCollection<Book> _booksCollection;
    
        public BooksService(
            IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
        {
            var mongoClient = new MongoClient(
                bookStoreDatabaseSettings.Value.ConnectionString);
    
            var mongoDatabase = mongoClient.GetDatabase(
                bookStoreDatabaseSettings.Value.DatabaseName);
    
            _booksCollection = mongoDatabase.GetCollection<Book>(
                bookStoreDatabaseSettings.Value.BooksCollectionName);
        }
    
        public async Task<List<Book>> GetAsync() =>
            await _booksCollection.Find(_ => true).ToListAsync();
    
        public async Task<Book?> GetAsync(string id) =>
            await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync();
    
        public async Task CreateAsync(Book newBook) =>
            await _booksCollection.InsertOneAsync(newBook);
    
        public async Task UpdateAsync(string id, Book updatedBook) =>
            await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook);
    
        public async Task RemoveAsync(string id) =>
            await _booksCollection.DeleteOneAsync(x => x.Id == id);
    }
    

    前述のコードでは、BookStoreDatabaseSettings インスタンスがコンストラクターの挿入によって DI から取得されます。 この手法で、「appsettings.json」セクションで追加した appsettings.json 構成値にアクセスできます。

  3. 次の強調表示されたコードを Program.cs に追加します。

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    
    builder.Services.AddSingleton<BooksService>();
    

    前述のコードでは、消費クラスへのコンストラクターの挿入をサポートする BooksService クラスが DI に登録されています。 BooksServiceMongoClient に直接依存しているため、シングルトン サービスの有効期間が最も適切です。 公式の Mongo Client 再利用ガイドラインに従い、シングルトン サービスの有効期間を使用して DI に MongoClient を登録する必要があります。

  4. 次のコードを Program.cs の先頭に追加して、BooksService の参照を解決します。

    using BookStoreApi.Services;
    

BooksService クラスは次の MongoDB.Driver メンバーを使用して、データベースに対する CRUD 操作を実行します。

  • MongoClient: データベース操作を実行するためにサーバー インスタンスを読み取ります。 このクラスのコンストラクターには MongoDB 接続文字列が提供されます。

    public BooksService(
        IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
    {
        var mongoClient = new MongoClient(
            bookStoreDatabaseSettings.Value.ConnectionString);
    
        var mongoDatabase = mongoClient.GetDatabase(
            bookStoreDatabaseSettings.Value.DatabaseName);
    
        _booksCollection = mongoDatabase.GetCollection<Book>(
            bookStoreDatabaseSettings.Value.BooksCollectionName);
    }
    
  • IMongoDatabase: 操作を実行する Mongo データベースを表します。 このチュートリアルでは、インターフェイスで汎用の GetCollection<TDocument>(collection) メソッドを使って、特定のコレクション内のデータにアクセスします。 このメソッドが呼び出された後に、CRUD 操作をこのコレクションに対して実行します。 GetCollection<TDocument>(collection) メソッドの呼び出しの内容は次のとおりです。

    • collection はコレクション名です。
    • TDocument はコレクションに格納されている CLR オブジェクト型です。

GetCollection<TDocument>(collection) から、コレクションを表す GetCollection<TDocument>(collection) オブジェクトが返されます。 このチュートリアルでは、コレクションに対して次のメソッドを呼び出します。

  • DeleteOneAsync: 指定された検索基準と一致する 1 つのドキュメントを削除します。
  • Find<TDocument>: 指定された検索基準と一致するコレクション内のすべてのドキュメントを返します。
  • InsertOneAsync: 指定されたオブジェクトを新しいドキュメントとしてコレクションに挿入します。
  • ReplaceOneAsync: 指定された検索基準と一致する 1 つのドキュメントを、指定されたオブジェクトで置き換えます。

コントローラーの追加

次のコードを使用して、BooksController クラスを BooksController ディレクトリに追加します。

using BookStoreApi.Models;
using BookStoreApi.Services;
using Microsoft.AspNetCore.Mvc;

namespace BookStoreApi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
    private readonly BooksService _booksService;

    public BooksController(BooksService booksService) =>
        _booksService = booksService;

    [HttpGet]
    public async Task<List<Book>> Get() =>
        await _booksService.GetAsync();

    [HttpGet("{id:length(24)}")]
    public async Task<ActionResult<Book>> Get(string id)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        return book;
    }

    [HttpPost]
    public async Task<IActionResult> Post(Book newBook)
    {
        await _booksService.CreateAsync(newBook);

        return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook);
    }

    [HttpPut("{id:length(24)}")]
    public async Task<IActionResult> Update(string id, Book updatedBook)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        updatedBook.Id = book.Id;

        await _booksService.UpdateAsync(id, updatedBook);

        return NoContent();
    }

    [HttpDelete("{id:length(24)}")]
    public async Task<IActionResult> Delete(string id)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        await _booksService.RemoveAsync(id);

        return NoContent();
    }
}

上記の Web API コントローラー:

  • BooksService クラスを使用して CRUD 操作を実行します。
  • GET、POST、PUT、DELETE HTTP 要求をサポートするアクション メソッドが含まれます。
  • Create アクション メソッドで CreatedAtAction を呼び出して、CreatedAtAction 応答を返します。 状態コード 201 は、サーバーに新しいリソースを作成する HTTP POST メソッドに対する標準の応答です。 CreatedAtAction によって、応答に Location ヘッダーも追加されます。 Location ヘッダーでは、新しく作成されたブックの URI を指定します。

Web API をテストする

  1. アプリケーションをビルドし、実行します。

  2. https://localhost:<port>/api/books に移動して、コントローラーのパラメーターなしの <port> アクション メソッドをテストします。ここで、Get は、自動的に割り当てられるアプリのポート番号です。 次のような JSON 応答が表示されます。

    [
      {
        "id": "61a6058e6c43f32854e51f51",
        "bookName": "Design Patterns",
        "price": 54.93,
        "category": "Computers",
        "author": "Ralph Johnson"
      },
      {
        "id": "61a6058e6c43f32854e51f52",
        "bookName": "Clean Code",
        "price": 43.15,
        "category": "Computers",
        "author": "Robert C. Martin"
      }
    ]
    
  3. https://localhost:<port>/api/books/{id here} に移動して、コントローラーのオーバーロードされた Get アクション メソッドをテストします。 次のような JSON 応答が表示されます。

    {
      "id": "61a6058e6c43f32854e51f52",
      "bookName": "Clean Code",
      "price": 43.15,
      "category": "Computers",
      "author": "Robert C. Martin"
    }
    

JSON シリアル化オプションを構成する

Web API をテストする」セクションで返されている JSON 応答について、変更すべき 2 つの詳細があります。

  • プロパティ名の既定の camel 形式は、CLR オブジェクトのプロパティ名の Pascal 形式と一致するように変更する必要があります
  • bookName プロパティは Name として返される必要があります。

上記の要件を満たすには、次の変更を行います。

  1. Program.cs で、次の強調表示されたコードを AddControllers メソッド呼び出しにチェーンします。

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    
    builder.Services.AddSingleton<BooksService>();
    
    builder.Services.AddControllers()
        .AddJsonOptions(
            options => options.JsonSerializerOptions.PropertyNamingPolicy = null);
    

    上記の変更により、Web API のシリアル化された JSON 応答内のプロパティ名は、CLR のオブジェクトの種類において対応するプロパティ名と一致しています。 たとえば、Book クラスの Author プロパティは、author ではなく Author としてシリアル化されます。

  2. Models/Book.cs では、[JsonPropertyName] 属性を使用して BookName プロパティに注釈を付けます。

    [BsonElement("Name")]
    [JsonPropertyName("Name")]
    public string BookName { get; set; } = null!;
    

    [JsonPropertyName] 属性の Name 値は、Web API のシリアル化された JSON 応答内のプロパティ名を表します。

  3. 次のコードを Models/Book.cs の先頭に追加して、[JsonProperty] 属性の参照を解決します。

    using System.Text.Json.Serialization;
    
  4. Web API をテストする」セクションで定義されている手順を繰り返します。 JSON プロパティ名の違いに注意してください。

Web API に認証サポートを追加

ASP.NET Core Identity では、ASP.NET Core Web アプリにユーザー インターフェイス (UI) ログイン機能が追加されます。 Web API と SPA をセキュリティで保護するには、次のいずれかを使用します。

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 サイト) を参照してください。

その他のリソース

このチュートリアルでは、MongoDB NoSQL データベース上で Create、Read、Update、Delete の各操作 (CRUD) を実行する Web API を作成します。

このチュートリアルでは、次の作業を行う方法について説明します。

  • MongoDB を構成する
  • MongoDB データベースを作成する
  • MongoDB のコレクションとスキーマを定義する
  • Web API から MongoDB CRUD 操作を実行する
  • JSON シリアル化をカスタマイズする

必須コンポーネント

MongoDB を構成する

開発用コンピューター上のどこからでも MongoDB および Mongo DB Shell のアクセスを有効にします。

  1. Windows の場合、既定では MongoDB が C:\Program Files\MongoDB にインストールされます。 C:\Program Files\MongoDB\Server\<version_number>\binPATH 環境変数に追加します。

  2. MongoDB Shell をダウンロードし、それを抽出するディレクトリを選択します。 mongosh.exe の作成されたパスを PATH 環境変数に追加します。

  3. データを格納するための開発用コンピューター上のディレクトリを選択します。 たとえば、Windows では C:\BooksData です。 存在しない場合はディレクトリを作成します。 mongo シェルでは新しいディレクトリは作成されません。

  4. OS のコマンド シェル (MongoDB Shell ではなく) で、次のコマンドを使って、既定のポート 27017 で MongoDB に接続します。 <data_directory_path> を、前の手順で選択したディレクトリに置き換えます。

    mongod --dbpath <data_directory_path>
    

次の手順では、以前にインストールした MongoDB シェルを使用して、データベースを作成し、コレクションを作成し、ドキュメントを保存します。 MongoDB シェル コマンドの詳細については、「mongosh」を参照してください。

  1. mongosh.exe を起動して、MongoDB コマンド シェル インスタンスを開きます。

  2. コマンド シェルで、次のコマンドを実行して、既定のテスト データベースに接続します。

    mongosh
    
  3. 次のコマンドをコマンド シェルで実行します。

    use BookStore
    

    BookStore という名前のデータベースが、まだ存在しない場合は作成されます。 データベースが存在する場合は、トランザクションのために接続されます。

  4. 次のコマンドを使用して Books コレクションを作成します。

    db.createCollection('Books')
    

    次のような結果が表示されます。

    { "ok" : 1 }
    
  5. 次のコマンドを使用して、Books コレクションのスキーマを定義し、2 つのドキュメントを挿入します。

    db.Books.insertMany([{ "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Name": "Clean Code", "Price": 43.15, "Category": "Computers","Author": "Robert C. Martin" }])
    

    次のような結果が表示されます。

    {
        "acknowledged" : true,
        "insertedIds" : [
            ObjectId("61a6058e6c43f32854e51f51"),
            ObjectId("61a6058e6c43f32854e51f52")
         ]
     }
    

    Note

    前述の結果に示されている ObjectId は、実際のコマンド シェルの表示内容とは一致しません。

  6. 次のコマンドを使用して、データベース内のドキュメントを表示します。

    db.Books.find().pretty()
    

    次のような結果が表示されます。

    {
         "_id" : ObjectId("61a6058e6c43f32854e51f51"),
         "Name" : "Design Patterns",
         "Price" : 54.93,
         "Category" : "Computers",
         "Author" : "Ralph Johnson"
     }
     {
         "_id" : ObjectId("61a6058e6c43f32854e51f52"),
         "Name" : "Clean Code",
         "Price" : 43.15,
         "Category" : "Computers",
         "Author" : "Robert C. Martin"
     }
    

    スキーマによって、自動生成された型が ObjectId_id プロパティが各ドキュメントに追加されます。

ASP.NET Core Web API プロジェクトを作成する

  1. [ファイル]>[新規]>[プロジェクト] の順に移動します。

  2. [ASP.NET Core Web API] プロジェクトの種類を選択し、[次へ] を選択します。

  3. プロジェクトに「BookStoreApi」という名前を付け、[次へ] を選択します。

  4. [.NET 6.0 (長期的なサポート)] フレームワークを選択し、[作成] を選択します。

  5. [パッケージ マネージャー コンソール] ウィンドウで、プロジェクトのルートに移動します。 次のコマンドを実行して、MongoDB 用の .NET ドライバーをインストールします。

    Install-Package MongoDB.Driver
    

モデルにエンティティを追加する

  1. Models ディレクトリをプロジェクトのルートに追加します。

  2. 次のコードを使用して、Book クラスを Book ディレクトリに追加します。

    using MongoDB.Bson;
    using MongoDB.Bson.Serialization.Attributes;
    
    namespace BookStoreApi.Models;
    
    public class Book
    {
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public string? Id { get; set; }
    
        [BsonElement("Name")]
        public string BookName { get; set; } = null!;
    
        public decimal Price { get; set; }
    
        public string Category { get; set; } = null!;
    
        public string Author { get; set; } = null!;
    }
    

    上記のクラスでは、Id プロパティは:

    • 共通言語ランタイム (CLR) オブジェクトを MongoDB コレクションにマッピングするために必須です。
    • このプロパティをドキュメントの主キーとするために、[BsonId] で注釈を付けられています。
    • [BsonRepresentation(BsonType.ObjectId)] 構造体ではなく string 型としてパラメーターを渡すことができるようにするために、[BsonRepresentation(BsonType.ObjectId)] で注釈を付けられています。 Mongo によって string から ObjectId への変換が処理されます。

    BookName プロパティには、[BsonElement] 属性を使用して注釈が付けられます。 属性の値 Name は、MongoDB コレクションでのプロパティ名を表します。

構成モデルを追加する

  1. 次のデータベース構成値を appsettings.json に追加します。

    {
        "BookStoreDatabase": {
            "ConnectionString": "mongodb://localhost:27017",
            "DatabaseName": "BookStore",
            "BooksCollectionName": "Books"
        },
        "Logging": {
            "LogLevel": {
                "Default": "Information",
                "Microsoft.AspNetCore": "Warning"
            }
        },
        "AllowedHosts": "*"
    }
    
  2. 次のコードを使用して、BookStoreDatabaseSettings クラスを BookStoreDatabaseSettings ディレクトリに追加します。

    namespace BookStoreApi.Models;
    
    public class BookStoreDatabaseSettings
    {
        public string ConnectionString { get; set; } = null!;
    
        public string DatabaseName { get; set; } = null!;
    
        public string BooksCollectionName { get; set; } = null!;
    }
    

    前述の BookStoreDatabaseSettings クラスは、 appsettings.json ファイルの BookStoreDatabase プロパティ値を格納するために使用されます。 JSON と C# のプロパティ名には、マッピング処理を簡単にするために同じ名前が付けられています。

  3. 次の強調表示されたコードを Program.cs に追加します。

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    

    上のコードで、appsettings.json ファイルの BookStoreDatabase セクションがバインドされている構成インスタンスは、Dependency Injection (DI) コンテナーに登録されています。 たとえば、BookStoreDatabaseSettings オブジェクトの ConnectionString プロパティには、appsettings.jsonBookStoreDatabase:ConnectionString プロパティが設定されています。

  4. 次のコードを Program.cs の先頭に追加して、BookStoreDatabaseSettings の参照を解決します。

    using BookStoreApi.Models;
    

CRUD 操作のサービスを追加する

  1. Services ディレクトリをプロジェクトのルートに追加します。

  2. 次のコードを使用して、BooksService クラスを BooksService ディレクトリに追加します。

    using BookStoreApi.Models;
    using Microsoft.Extensions.Options;
    using MongoDB.Driver;
    
    namespace BookStoreApi.Services;
    
    public class BooksService
    {
        private readonly IMongoCollection<Book> _booksCollection;
    
        public BooksService(
            IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
        {
            var mongoClient = new MongoClient(
                bookStoreDatabaseSettings.Value.ConnectionString);
    
            var mongoDatabase = mongoClient.GetDatabase(
                bookStoreDatabaseSettings.Value.DatabaseName);
    
            _booksCollection = mongoDatabase.GetCollection<Book>(
                bookStoreDatabaseSettings.Value.BooksCollectionName);
        }
    
        public async Task<List<Book>> GetAsync() =>
            await _booksCollection.Find(_ => true).ToListAsync();
    
        public async Task<Book?> GetAsync(string id) =>
            await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync();
    
        public async Task CreateAsync(Book newBook) =>
            await _booksCollection.InsertOneAsync(newBook);
    
        public async Task UpdateAsync(string id, Book updatedBook) =>
            await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook);
    
        public async Task RemoveAsync(string id) =>
            await _booksCollection.DeleteOneAsync(x => x.Id == id);
    }
    

    前述のコードでは、BookStoreDatabaseSettings インスタンスがコンストラクターの挿入によって DI から取得されます。 この手法で、「appsettings.json」セクションで追加した appsettings.json 構成値にアクセスできます。

  3. 次の強調表示されたコードを Program.cs に追加します。

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    
    builder.Services.AddSingleton<BooksService>();
    

    前述のコードでは、消費クラスへのコンストラクターの挿入をサポートする BooksService クラスが DI に登録されています。 BooksServiceMongoClient に直接依存しているため、シングルトン サービスの有効期間が最も適切です。 公式の Mongo Client 再利用ガイドラインに従い、シングルトン サービスの有効期間を使用して DI に MongoClient を登録する必要があります。

  4. 次のコードを Program.cs の先頭に追加して、BooksService の参照を解決します。

    using BookStoreApi.Services;
    

BooksService クラスは次の MongoDB.Driver メンバーを使用して、データベースに対する CRUD 操作を実行します。

  • MongoClient: データベース操作を実行するためにサーバー インスタンスを読み取ります。 このクラスのコンストラクターには MongoDB 接続文字列が提供されます。

    public BooksService(
        IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
    {
        var mongoClient = new MongoClient(
            bookStoreDatabaseSettings.Value.ConnectionString);
    
        var mongoDatabase = mongoClient.GetDatabase(
            bookStoreDatabaseSettings.Value.DatabaseName);
    
        _booksCollection = mongoDatabase.GetCollection<Book>(
            bookStoreDatabaseSettings.Value.BooksCollectionName);
    }
    
  • IMongoDatabase: 操作を実行する Mongo データベースを表します。 このチュートリアルでは、インターフェイスで汎用の GetCollection<TDocument>(collection) メソッドを使って、特定のコレクション内のデータにアクセスします。 このメソッドが呼び出された後に、CRUD 操作をこのコレクションに対して実行します。 GetCollection<TDocument>(collection) メソッドの呼び出しの内容は次のとおりです。

    • collection はコレクション名です。
    • TDocument はコレクションに格納されている CLR オブジェクト型です。

GetCollection<TDocument>(collection) から、コレクションを表す GetCollection<TDocument>(collection) オブジェクトが返されます。 このチュートリアルでは、コレクションに対して次のメソッドを呼び出します。

  • DeleteOneAsync: 指定された検索基準と一致する 1 つのドキュメントを削除します。
  • Find<TDocument>: 指定された検索基準と一致するコレクション内のすべてのドキュメントを返します。
  • InsertOneAsync: 指定されたオブジェクトを新しいドキュメントとしてコレクションに挿入します。
  • ReplaceOneAsync: 指定された検索基準と一致する 1 つのドキュメントを、指定されたオブジェクトで置き換えます。

コントローラーの追加

次のコードを使用して、BooksController クラスを BooksController ディレクトリに追加します。

using BookStoreApi.Models;
using BookStoreApi.Services;
using Microsoft.AspNetCore.Mvc;

namespace BookStoreApi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
    private readonly BooksService _booksService;

    public BooksController(BooksService booksService) =>
        _booksService = booksService;

    [HttpGet]
    public async Task<List<Book>> Get() =>
        await _booksService.GetAsync();

    [HttpGet("{id:length(24)}")]
    public async Task<ActionResult<Book>> Get(string id)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        return book;
    }

    [HttpPost]
    public async Task<IActionResult> Post(Book newBook)
    {
        await _booksService.CreateAsync(newBook);

        return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook);
    }

    [HttpPut("{id:length(24)}")]
    public async Task<IActionResult> Update(string id, Book updatedBook)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        updatedBook.Id = book.Id;

        await _booksService.UpdateAsync(id, updatedBook);

        return NoContent();
    }

    [HttpDelete("{id:length(24)}")]
    public async Task<IActionResult> Delete(string id)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        await _booksService.RemoveAsync(id);

        return NoContent();
    }
}

上記の Web API コントローラー:

  • BooksService クラスを使用して CRUD 操作を実行します。
  • GET、POST、PUT、DELETE HTTP 要求をサポートするアクション メソッドが含まれます。
  • Create アクション メソッドで CreatedAtAction を呼び出して、CreatedAtAction 応答を返します。 状態コード 201 は、サーバーに新しいリソースを作成する HTTP POST メソッドに対する標準の応答です。 CreatedAtAction によって、応答に Location ヘッダーも追加されます。 Location ヘッダーでは、新しく作成されたブックの URI を指定します。

Web API をテストする

  1. アプリケーションをビルドし、実行します。

  2. https://localhost:<port>/api/books に移動して、コントローラーのパラメーターなしの <port> アクション メソッドをテストします。ここで、Get は、自動的に割り当てられるアプリのポート番号です。 次のような JSON 応答が表示されます。

    [
      {
        "id": "61a6058e6c43f32854e51f51",
        "bookName": "Design Patterns",
        "price": 54.93,
        "category": "Computers",
        "author": "Ralph Johnson"
      },
      {
        "id": "61a6058e6c43f32854e51f52",
        "bookName": "Clean Code",
        "price": 43.15,
        "category": "Computers",
        "author": "Robert C. Martin"
      }
    ]
    
  3. https://localhost:<port>/api/books/{id here} に移動して、コントローラーのオーバーロードされた Get アクション メソッドをテストします。 次のような JSON 応答が表示されます。

    {
      "id": "61a6058e6c43f32854e51f52",
      "bookName": "Clean Code",
      "price": 43.15,
      "category": "Computers",
      "author": "Robert C. Martin"
    }
    

JSON シリアル化オプションを構成する

Web API をテストする」セクションで返されている JSON 応答について、変更すべき 2 つの詳細があります。

  • プロパティ名の既定の camel 形式は、CLR オブジェクトのプロパティ名の Pascal 形式と一致するように変更する必要があります
  • bookName プロパティは Name として返される必要があります。

上記の要件を満たすには、次の変更を行います。

  1. Program.cs で、次の強調表示されたコードを AddControllers メソッド呼び出しにチェーンします。

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    
    builder.Services.AddSingleton<BooksService>();
    
    builder.Services.AddControllers()
        .AddJsonOptions(
            options => options.JsonSerializerOptions.PropertyNamingPolicy = null);
    

    上記の変更により、Web API のシリアル化された JSON 応答内のプロパティ名は、CLR のオブジェクトの種類において対応するプロパティ名と一致しています。 たとえば、Book クラスの Author プロパティは、author ではなく Author としてシリアル化されます。

  2. Models/Book.cs では、[JsonPropertyName] 属性を使用して BookName プロパティに注釈を付けます。

    [BsonElement("Name")]
    [JsonPropertyName("Name")]
    public string BookName { get; set; } = null!;
    

    [JsonPropertyName] 属性の Name 値は、Web API のシリアル化された JSON 応答内のプロパティ名を表します。

  3. 次のコードを Models/Book.cs の先頭に追加して、[JsonProperty] 属性の参照を解決します。

    using System.Text.Json.Serialization;
    
  4. Web API をテストする」セクションで定義されている手順を繰り返します。 JSON プロパティ名の違いに注意してください。

Web API に認証サポートを追加

ASP.NET Core Identity では、ASP.NET Core Web アプリにユーザー インターフェイス (UI) ログイン機能が追加されます。 Web API と SPA をセキュリティで保護するには、次のいずれかを使用します。

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 サイト) を参照してください。

その他のリソース

このチュートリアルでは、MongoDB NoSQL データベース上で Create、Read、Update、Delete の各操作 (CRUD) を実行する Web API を作成します。

このチュートリアルでは、次の作業を行う方法について説明します。

  • MongoDB を構成する
  • MongoDB データベースを作成する
  • MongoDB のコレクションとスキーマを定義する
  • Web API から MongoDB CRUD 操作を実行する
  • JSON シリアル化をカスタマイズする

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

必須コンポーネント

MongoDB を構成する

Windows を使用する場合、既定では MongoDB は C:\Program Files\MongoDB にインストールされます。 C:\Program Files\MongoDB\Server\<version_number>\binPath 環境変数に追加します。 この変更により、開発用コンピューターのどこからでも MongoDB にアクセスできるようになります。

次の手順では mongo シェルを使用して、データベースを作成し、コレクションを作成し、ドキュメントを保存します。 mongo のシェル コマンドについて詳しくは、「Working with the mongo Shell」(mongo シェルの使用) をご覧ください。

  1. データを格納するために開発用コンピューター上のディレクトリを選択します。 たとえば、Windows では C:\BooksData です。 存在しない場合はディレクトリを作成します。 mongo シェルでは新しいディレクトリは作成されません。

  2. コマンド シェルを開きます。 次のコマンドを実行して、既定のポート 27017 で MongoDB に接続します。 忘れずに、前の手順で選択したディレクトリで <data_directory_path> を置き換えます。

    mongod --dbpath <data_directory_path>
    
  3. 別のコマンド シェル インスタンスを開きます。 次のコマンドを実行して、既定のテスト データベースに接続します。

    mongo
    
  4. コマンド シェルで次のコマンドを実行します。

    use BookstoreDb
    

    BookstoreDb という名前のデータベースが、まだ存在していない場合は作成されます。 データベースが存在する場合は、トランザクションのために接続されます。

  5. 次のコマンドを使用して Books コレクションを作成します。

    db.createCollection('Books')
    

    次のような結果が表示されます。

    { "ok" : 1 }
    
  6. 次のコマンドを使用して、Books コレクションのスキーマを定義し、2 つのドキュメントを挿入します。

    db.Books.insertMany([{'Name':'Design Patterns','Price':54.93,'Category':'Computers','Author':'Ralph Johnson'}, {'Name':'Clean Code','Price':43.15,'Category':'Computers','Author':'Robert C. Martin'}])
    

    次のような結果が表示されます。

    {
      "acknowledged" : true,
      "insertedIds" : [
        ObjectId("5bfd996f7b8e48dc15ff215d"),
        ObjectId("5bfd996f7b8e48dc15ff215e")
      ]
    }
    

    Note

    この記事に示されている ID は、このサンプルを実行するときの ID とは一致していません。

  7. 次のコマンドを使用して、データベース内のドキュメントを表示します。

    db.Books.find({}).pretty()
    

    次のような結果が表示されます。

    {
      "_id" : ObjectId("5bfd996f7b8e48dc15ff215d"),
      "Name" : "Design Patterns",
      "Price" : 54.93,
      "Category" : "Computers",
      "Author" : "Ralph Johnson"
    }
    {
      "_id" : ObjectId("5bfd996f7b8e48dc15ff215e"),
      "Name" : "Clean Code",
      "Price" : 43.15,
      "Category" : "Computers",
      "Author" : "Robert C. Martin"
    }
    

    スキーマによって、自動生成された型が ObjectId_id プロパティが各ドキュメントに追加されます。

データベースの準備ができました。 ASP.NET Core Web API の作成を開始できます。

ASP.NET Core Web API プロジェクトを作成する

  1. [ファイル]>[新規]>[プロジェクト] の順に移動します。

  2. [ASP.NET Core Web アプリケーション] プロジェクトの種類を選択し、 [次へ] を選択します。

  3. プロジェクトに BooksApi という名前を付けて、 [作成] を選択します。

  4. [.NET Core] ターゲット フレームワークと [ASP.NET Core 3.0] を選択します。 [API] プロジェクト テンプレートを選択し、 [作成] を選択します。

  5. NuGet ギャラリー:MongoDB.Driver に関するページを参照して、MongoDB 用 .NET ドライバーの最新の安定バージョンを確認します。 [パッケージ マネージャー コンソール] ウィンドウで、プロジェクトのルートに移動します。 次のコマンドを実行して、MongoDB 用の .NET ドライバーをインストールします。

    Install-Package MongoDB.Driver -Version {VERSION}
    

モデルにエンティティを追加する

  1. Models ディレクトリをプロジェクトのルートに追加します。

  2. 次のコードを使用して、Book クラスを Book ディレクトリに追加します。

    using MongoDB.Bson;
    using MongoDB.Bson.Serialization.Attributes;
    
    namespace BooksApi.Models
    {
        public class Book
        {
            [BsonId]
            [BsonRepresentation(BsonType.ObjectId)]
            public string Id { get; set; }
    
            [BsonElement("Name")]
            public string BookName { get; set; }
    
            public decimal Price { get; set; }
    
            public string Category { get; set; }
    
            public string Author { get; set; }
        }
    }
    

    上記のクラスでは、Id プロパティは:

    • 共通言語ランタイム (CLR) オブジェクトを MongoDB コレクションにマッピングするために必須です。
    • このプロパティをドキュメントの主キーとするために、[BsonId] で注釈を付けられています。
    • [BsonRepresentation(BsonType.ObjectId)] 構造体ではなく string 型としてパラメーターを渡すことができるようにするために、[BsonRepresentation(BsonType.ObjectId)] で注釈を付けられています。 Mongo によって string から ObjectId への変換が処理されます。

    BookName プロパティには、[BsonElement] 属性を使用して注釈が付けられます。 属性の値 Name は、MongoDB コレクションでのプロパティ名を表します。

構成モデルを追加する

  1. 次のデータベース構成値を appsettings.json に追加します。

    {
      "BookstoreDatabaseSettings": {
        "BooksCollectionName": "Books",
        "ConnectionString": "mongodb://localhost:27017",
        "DatabaseName": "BookstoreDb"
      },
      "Logging": {
        "IncludeScopes": false,
        "Debug": {
          "LogLevel": {
            "Default": "Warning"
          }
        },
        "Console": {
          "LogLevel": {
            "Default": "Warning"
          }
        }
      }
    }
    
  2. 次のコードを使用して、BookstoreDatabaseSettings.cs ファイルを Models ディレクトリに追加します。

    namespace BooksApi.Models
    {
        public class BookstoreDatabaseSettings : IBookstoreDatabaseSettings
        {
            public string BooksCollectionName { get; set; }
            public string ConnectionString { get; set; }
            public string DatabaseName { get; set; }
        }
    
        public interface IBookstoreDatabaseSettings
        {
            string BooksCollectionName { get; set; }
            string ConnectionString { get; set; }
            string DatabaseName { get; set; }
        }
    }
    

    前述の BookstoreDatabaseSettings クラスは、 appsettings.json ファイルの BookstoreDatabaseSettings プロパティ値を格納するために使用されます。 JSON と C# のプロパティ名には、マッピング処理を簡単にするために同じ名前が付けられています。

  3. 次の強調表示されたコードを Startup.ConfigureServices に追加します。

    public void ConfigureServices(IServiceCollection services)
    {
        // requires using Microsoft.Extensions.Options
        services.Configure<BookstoreDatabaseSettings>(
            Configuration.GetSection(nameof(BookstoreDatabaseSettings)));
    
        services.AddSingleton<IBookstoreDatabaseSettings>(sp =>
            sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value);
    
        services.AddControllers();
    }
    

    上のコードでは以下の操作が行われます。

    • appsettings.json ファイルの BookstoreDatabaseSettings セクションがバインドされている構成インスタンスは、Dependency Injection (DI) コンテナーに登録されています。 たとえば、BookstoreDatabaseSettings オブジェクトの ConnectionString プロパティには、 appsettings.jsonBookstoreDatabaseSettings:ConnectionString プロパティが設定されています。
    • IBookstoreDatabaseSettings インターフェイスは、シングルトン IBookstoreDatabaseSettingsで DI に登録されています。 挿入されると、インターフェイス インスタンスは BookstoreDatabaseSettings オブジェクトに解決されます。
  4. 次のコードを Startup.cs の先頭に追加して、BookstoreDatabaseSettingsIBookstoreDatabaseSettings の参照を解決します。

    using BooksApi.Models;
    

CRUD 操作のサービスを追加する

  1. Services ディレクトリをプロジェクトのルートに追加します。

  2. 次のコードを使用して、BookService クラスを BookService ディレクトリに追加します。

    using BooksApi.Models;
    using MongoDB.Driver;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace BooksApi.Services
    {
        public class BookService
        {
            private readonly IMongoCollection<Book> _books;
    
            public BookService(IBookstoreDatabaseSettings settings)
            {
                var client = new MongoClient(settings.ConnectionString);
                var database = client.GetDatabase(settings.DatabaseName);
    
                _books = database.GetCollection<Book>(settings.BooksCollectionName);
            }
    
            public List<Book> Get() =>
                _books.Find(book => true).ToList();
    
            public Book Get(string id) =>
                _books.Find<Book>(book => book.Id == id).FirstOrDefault();
    
            public Book Create(Book book)
            {
                _books.InsertOne(book);
                return book;
            }
    
            public void Update(string id, Book bookIn) =>
                _books.ReplaceOne(book => book.Id == id, bookIn);
    
            public void Remove(Book bookIn) =>
                _books.DeleteOne(book => book.Id == bookIn.Id);
    
            public void Remove(string id) => 
                _books.DeleteOne(book => book.Id == id);
        }
    }
    

    前述のコードでは、IBookstoreDatabaseSettings インスタンスがコンストラクターの挿入によって DI から取得されます。 この手法で、「appsettings.json」セクションで追加した appsettings.json 構成値にアクセスできます。

  3. 次の強調表示されたコードを Startup.ConfigureServices に追加します。

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<BookstoreDatabaseSettings>(
            Configuration.GetSection(nameof(BookstoreDatabaseSettings)));
    
        services.AddSingleton<IBookstoreDatabaseSettings>(sp =>
            sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value);
    
        services.AddSingleton<BookService>();
    
        services.AddControllers();
    }
    

    前述のコードでは、消費クラスへのコンストラクターの挿入をサポートする BookService クラスが DI に登録されています。 BookServiceMongoClient に直接依存しているため、シングルトン サービスの有効期間が最も適切です。 公式の Mongo Client 再利用ガイドラインに従い、シングルトン サービスの有効期間を使用して DI に MongoClient を登録する必要があります。

  4. 次のコードを Startup.cs の先頭に追加して、BookService の参照を解決します。

    using BooksApi.Services;
    

BookService クラスは次の MongoDB.Driver メンバーを使用して、データベースに対する CRUD 操作を実行します。

  • MongoClient: データベース操作を実行するためにサーバー インスタンスを読み取ります。 このクラスのコンストラクターには MongoDB 接続文字列が提供されます。

    public BookService(IBookstoreDatabaseSettings settings)
    {
        var client = new MongoClient(settings.ConnectionString);
        var database = client.GetDatabase(settings.DatabaseName);
    
        _books = database.GetCollection<Book>(settings.BooksCollectionName);
    }
    
  • IMongoDatabase: 操作を実行する Mongo データベースを表します。 このチュートリアルでは、インターフェイスで汎用の GetCollection<TDocument>(collection) メソッドを使って、特定のコレクション内のデータにアクセスします。 このメソッドが呼び出された後に、CRUD 操作をこのコレクションに対して実行します。 GetCollection<TDocument>(collection) メソッドの呼び出しの内容は次のとおりです。

    • collection はコレクション名です。
    • TDocument はコレクションに格納されている CLR オブジェクト型です。

GetCollection<TDocument>(collection) から、コレクションを表す GetCollection<TDocument>(collection) オブジェクトが返されます。 このチュートリアルでは、コレクションに対して次のメソッドを呼び出します。

  • DeleteOne:指定された検索基準と一致する 1 つのドキュメントを削除します。
  • Find<TDocument>: 指定された検索基準と一致するコレクション内のすべてのドキュメントを返します。
  • InsertOne:指定されたオブジェクトを新しいドキュメントとしてコレクションに挿入します。
  • ReplaceOne:指定された検索基準と一致する 1 つのドキュメントを、指定されたオブジェクトで置き換えます。

コントローラーの追加

次のコードを使用して、BooksController クラスを BooksController ディレクトリに追加します。

using BooksApi.Models;
using BooksApi.Services;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace BooksApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BooksController : ControllerBase
    {
        private readonly BookService _bookService;

        public BooksController(BookService bookService)
        {
            _bookService = bookService;
        }

        [HttpGet]
        public ActionResult<List<Book>> Get() =>
            _bookService.Get();

        [HttpGet("{id:length(24)}", Name = "GetBook")]
        public ActionResult<Book> Get(string id)
        {
            var book = _bookService.Get(id);

            if (book == null)
            {
                return NotFound();
            }

            return book;
        }

        [HttpPost]
        public ActionResult<Book> Create(Book book)
        {
            _bookService.Create(book);

            return CreatedAtRoute("GetBook", new { id = book.Id.ToString() }, book);
        }

        [HttpPut("{id:length(24)}")]
        public IActionResult Update(string id, Book bookIn)
        {
            var book = _bookService.Get(id);

            if (book == null)
            {
                return NotFound();
            }

            _bookService.Update(id, bookIn);

            return NoContent();
        }

        [HttpDelete("{id:length(24)}")]
        public IActionResult Delete(string id)
        {
            var book = _bookService.Get(id);

            if (book == null)
            {
                return NotFound();
            }

            _bookService.Remove(id);

            return NoContent();
        }
    }
}

上記の Web API コントローラー:

  • BookService クラスを使用して CRUD 操作を実行します。
  • GET、POST、PUT、DELETE HTTP 要求をサポートするアクション メソッドが含まれます。
  • Create アクション メソッドで CreatedAtRoute を呼び出して、CreatedAtRoute 応答を返します。 状態コード 201 は、サーバーに新しいリソースを作成する HTTP POST メソッドに対する標準の応答です。 CreatedAtRoute によって、応答に Location ヘッダーも追加されます。 Location ヘッダーでは、新しく作成されたブックの URI を指定します。

Web API をテストする

  1. アプリケーションをビルドし、実行します。

  2. https://localhost:<port>/api/books に移動して、コントローラーのパラメーターなしの Get アクション メソッドをテストします。 次の JSON 応答が示されます。

    [
      {
        "id":"5bfd996f7b8e48dc15ff215d",
        "bookName":"Design Patterns",
        "price":54.93,
        "category":"Computers",
        "author":"Ralph Johnson"
      },
      {
        "id":"5bfd996f7b8e48dc15ff215e",
        "bookName":"Clean Code",
        "price":43.15,
        "category":"Computers",
        "author":"Robert C. Martin"
      }
    ]
    
  3. https://localhost:<port>/api/books/{id here} に移動して、コントローラーのオーバーロードされた Get アクション メソッドをテストします。 次の JSON 応答が示されます。

    {
      "id":"{ID}",
      "bookName":"Clean Code",
      "price":43.15,
      "category":"Computers",
      "author":"Robert C. Martin"
    }
    

JSON シリアル化オプションを構成する

Web API をテストする」セクションで返されている JSON 応答について、変更すべき 2 つの詳細があります。

  • プロパティ名の既定の camel 形式は、CLR オブジェクトのプロパティ名の Pascal 形式と一致するように変更する必要があります
  • bookName プロパティは Name として返される必要があります。

上記の要件を満たすには、次の変更を行います。

  1. Json.NET は ASP.NET 共有フレームワークから削除されています。 Microsoft.AspNetCore.Mvc.NewtonsoftJson へのパッケージ参照を追加します。

  2. Startup.ConfigureServices で、次の強調表示されたコードを AddControllers メソッド呼び出しにチェーンします。

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<BookstoreDatabaseSettings>(
            Configuration.GetSection(nameof(BookstoreDatabaseSettings)));
    
        services.AddSingleton<IBookstoreDatabaseSettings>(sp =>
            sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value);
    
        services.AddSingleton<BookService>();
    
        services.AddControllers()
            .AddNewtonsoftJson(options => options.UseMemberCasing());
    }
    

    上記の変更により、Web API のシリアル化された JSON 応答内のプロパティ名は、CLR のオブジェクトの種類において対応するプロパティ名と一致しています。 たとえば、Book クラスの Author プロパティは Author としてシリアル化されます。

  3. Models/Book.csModels/Book.cs では、Models/Book.cs プロパティに次の [JsonProperty] 属性を使用して注釈を付けます。

    [BsonElement("Name")]
    [JsonProperty("Name")]
    public string BookName { get; set; }
    

    [JsonProperty] 属性の Name 値は、Web API のシリアル化された JSON 応答内のプロパティ名を表します。

  4. 次のコードを Models/Book.cs の先頭に追加して、[JsonProperty] 属性の参照を解決します。

    using Newtonsoft.Json;
    
  5. Web API をテストする」セクションで定義されている手順を繰り返します。 JSON プロパティ名の違いに注意してください。

Web API に認証サポートを追加

ASP.NET Core Identity では、ASP.NET Core Web アプリにユーザー インターフェイス (UI) ログイン機能が追加されます。 Web API と SPA をセキュリティで保護するには、次のいずれかを使用します。

Duende IdentityServer は、ASP.NET Core 用の OpenID Connect および OAuth 2.0 フレームワークです。 Duende IdentityServer により、次のセキュリティ機能が有効になります。

  • サービスとしての認証 (AaaS)
  • 複数のアプリケーションの種類でのシングル サインオン/オフ (SSO)
  • API のアクセス制御
  • Federation Gateway

詳細については、Duende IdentityServer の概要に関するページを参照してください。

その他の認証プロバイダーについて詳しくは、「ASP.NET Core のコミュニティ OSS 認証オプション」を参照してください

次の手順

ASP.NET Core Web API の構築について詳しくは、次のリソースを参照してください。