Minimal APIs は .NET 6 で導入された興味深い機能で、API の作成方法を革新することを目的としています。

最小限のコードとゼロの定型コードで堅牢な API を構築する想像をしてみてください—もうコントローラー、ルーティング、ミドルウェアと格闘する必要はありません。それが Minimal APIs が可能にするものです。これらの API のアイデアは、開発プロセスを合理化し、非常に簡単で効率的にすることです。

この記事では、.NET 8 の Minimal APIs の世界に飛び込み、完全に機能する書店 API を作成する方法をガイドします。すべての本を取得する方法、ID で本を検索する方法、新しい本を追加する方法、そして本を削除する方法を学びます。始めましょう。

目次

前提条件

始める前に、以下の前提条件がお使いのマシンにインストールされていることを確認してください:

代わりに、Visual Studio 2022を使用することもできます。これは.NET 8のサポートが組み込まれています。しかし、この記事ではVisual Studio Codeを使用します。軽量で使いやすく、クロスプラットフォームです。

APIのテストにはSwagger UIを使用します。Swagger UIは、ブラウザから直接APIと対話できる強力なツールです。APIエンドポイントをテストするためのユーザーフレンドリーなインターフェースを提供し、APIのテストとデバッグを容易にします。

新しいプロジェクトを作成すると、必要なパッケージが自動的にインストールされ、Swagger UIを使用するようにプロジェクトが設定されます。.NET 8にはデフォルトでSwagger UIが含まれているため、Visual Studioでアプリケーションを作成するか、.NETを使用するかに関わらず、Swagger UIが設定されます。

アプリケーションを実行すると、Swagger UIがブラウザで自動的に開きますが、VS Codeを使用しているため、ターミナルのポート番号をクリックする必要があります。

このプロジェクトのソースコードはGitHubで見つけることができます。

ミニマルAPI入門

多数のエンドポイントがあり、非常に大きく複雑なコードベースで作業していることを想像してみてください。伝統的に、ASP.NET CoreでAPIを構築するには、コントローラー、ルーティング、ミドルウェア、そして大量のボイラープレートコードを使用します。しかし、ASP.NET CoreでAPIを構築する方法には、伝統的な方法とミニマルな方法の2つがあります。

伝統的な方法はほとんどの開発者に馴染みがあり、コントローラーと広範なインフラストラクチャコードを含みます。ミニマルな方法は、.NET 6で導入され、最小限のコードとゼロのボイラープレートでAPIを作成できるようにします。このアプローチは開発プロセスを簡素化し、インフラストラクチャコードに対応する代わりにビジネスロジックの記述に集中できるようにします。

ミニマルAPIは軽量で高速であり、小規模から中規模のAPI構築に最適です。プロトタイピング、マイクロサービスの構築、または複雑さをあまり必要としないシンプルなAPIの作成に理想的です。このハンドブックでは、.NET 6でのミニマルAPIの世界を探求し、ゼロから完全に機能する書店APIを作成する方法を学びます。

ミニマルAPIの作成方法

dotnet CLIを使用してミニマルAPIを作成するのは簡単で、デフォルトのテンプレートはすでにミニマルAPIです。しかし、Visual Studioを使用する場合は、プロジェクトテンプレートに含まれるボイラープレートコードを削除する必要があります。

まずはdotnet CLIを使用して最小限のAPIプロジェクトを作成しましょう。


dotnet new webapi  -n BookStoreApi

dotnet new webapiコマンドで新しい最小限のAPIプロジェクトBookStoreApiが作成されます。このプロジェクトには、始めるために必要なファイルとフォルダが含まれています。

プロジェクトの構造を探ってみましょう:

  • Program.cs:アプリケーションのエントリーポイントで、ホストの設定が行われます。

  • bookapi-minimal.sln:プロジェクトを含むソリューションファイルです。

  • bookapi-minimal.http:APIをテストするためのサンプルHTTPリクエストが含まれるファイルです。

  • bookapi-minimal.csproj:プロジェクトの設定が含まれるプロジェクトファイルです。

  • appsettings.json:アプリケーションの設定を保存する設定ファイルです。

  • appsettings.Development.json:開発環境用の設定ファイルです。

プログラム.csファイルを開くと、コードが最小限であることに気づくでしょう。Program.csファイルには以下のコードが含まれています:


var builder = WebApplication.CreateBuilder(args);

// コンテナーにサービスを追加します。
// Swagger/OpenAPIの設定について詳しくは、https://aka.ms/aspnetcore/swashbuckle を参照してください
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// HTTP要求パイプラインを構成します。
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
    var forecast =  Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();

app.Run();

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

まだコードを完全に理解していない場合でも心配しないでください—今後のセクションで詳しく説明します。重要なポイントは、最小限のAPIは非常に少ないコードで済むことです。これがその主要な利点の一つです。

デフォルトのコードは、セットアップをテストするために使用できるシンプルな天気予報APIを設定します。これは天気予報のリストを生成し、/weatherforecastエンドポイントに対してGETリクエストを行うとそれらを返します。また、コードにはAPIをテストするためのSwagger UIも含まれています。

app.MapGetメソッドに特に注意してください。これはルートをハンドラ関数にマッピングします。この場合、/weatherforecastルートを天気予報のリストを返す関数にマッピングしています。次のセクションでは、同様のメソッドを使用して独自のエンドポイントを作成します。

プロジェクトのフォルダ構造を作成する前に、コントローラーベースとミニマルAPIの両方でのHTTPメソッドについて理解しましょう。

コントローラーベースとミニマルAPIのHTTPメソッド

コントローラーベースのアプローチは、Web APIを作成する従来の方法で、コントローラークラスを作成し、各HTTPメソッドに対してメソッドを定義する必要があります。例えば:

  • 「GET」メソッドを作成するには、「[HttpGet]」属性を使用します。

  • 「POST」メソッドを作成するには、「[HttpPost]」属性を使用します。

  • 「PUT」メソッドを作成するには、「[HttpPut]」属性を使用します。

  • 「DELETE」メソッドを作成するには、「[HttpDelete]」属性を使用します。

これがコントローラーベースのアプローチでエンドポイントが作成される方法です。

対照的に、ミニマルAPIは「app.MapGet」、「app.MapPost」、「app.MapPut」、「app.MapDelete」などのメソッドを使用してエンドポイントを作成します。これが 두つのアプローチの主な違いです:コントローラーベースのAPIは属性を使用してエンドポイントを定義するのに対し、ミニマルAPIはメソッドを使用します。

これでコントローラーベースとミニマルAPIの両方でHTTPリクエストを処理する方法を理解したので、プロジェクトのフォルダ構造を作成しましょう。

プロジェクトのフォルダ構造を作成する前に、まずは今持っているものを実行してみましょう。以前学んだように、Visual Studioまたは.NET CLIでプロジェクトを作成すると、デフォルトのWeatherForecastプロジェクトが付属しており、これを実行してUIで確認することができます。それを実行して、すべてが正常に動作することを確認してから、プロジェクトフォルダの作成に進みましょう。

次のコマンドを実行します:


dotnet run

以下の出力が表示されるはずです:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5228
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\Devolopemnt\Dotnet\bookapi-minimal

これは、アプリケーションが実行中であり、http://localhost:5228でリスニングしていることを意味します。上述したように、dotnet CLIとVisual Studio Codeを使用しているため、アプリケーションはブラウザを自動的に開いてくれません。これを手動で行う必要があります。

ブラウザを開き、http://localhost:5228/swagger/index.htmlに移動して、APIからのデフォルトのレスポンスを確認します。

次のようなものが表示されるはずです:

次にやるべきことは、プロジェクトを整理し、必要なファイルとフォルダを作成して、スタートするための準備をすることです。

ミニマルAPIプロジェクトファイル

プロジェクトを整理するために、構造化されたフォルダ階層を作成します。これにより、コードをクリーンに保ち、保守しやすくなります。以下は使用するフォルダ構造です:

  • AppContext: データベースコンテキストおよび関連する設定を含みます。

  • 設定: Entity Framework Coreの設定およびデータベースのシードデータを保持します。

  • 契約: 私たちのアプリケーションで使用されるデータ転送オブジェクト(DTO)を含みます。

  • エンドポイント: ここで私たちのミニマルAPIエンドポイントを定義および設定します。

  • 例外: プロジェクトで使用されるカスタム例外クラスを含みます。

  • 拡張: プロジェクト全体で使用する拡張メソッドを保持します。

  • モデル: ビジネスロジックモデルを含みます。

  • サービス: ビジネスロジックを実装するサービスクラスを含みます。

  • インターフェース: 私たちのサービスをマッピングするためのインターフェース定義を保持します。

Visual Studio Codeでは、以下のようにこのフォルダ構造を作成できます:

- AppContext
- Configurations
- Contracts
- Endpoints
- Exceptions
- Extensions
- Models
- Services
- Interfaces

設定後、プロジェクトのフォルダ構造は以下のようになるはずです:

プロジェクト構造が設定されたので、コードの記述を始めることができます。まずはモデルの作成から始めましょう。

モデルの作成方法

このセクションでは、アプリケーションのモデルを作成します。モデルはアプリケーションの構成要素であり、アプリケーションが扱うデータを表します。例として、本のモデルを作成します。

始めるために、プロジェクトディレクトリにModelsという名前のフォルダを作成します。このフォルダ内に、BookModel.csという名前のファイルを作成し、以下のコードを追加します:

// Models/BookModel.cs


namespace bookapi_minimal.Models
{
    public class BookModel
    {
        public Guid Id { get; set; }
        public string Title { get; set; }
        public string Author { get; set; }
        public string Description { get; set; }
        public string Category { get; set; }
        public string Language { get; set; }
        public int TotalPages { get; set; }
    }
}

このBookModelクラスは、本の詳細を表すプロパティを定義します。たとえば、titleauthordescriptioncategorylanguage、およびtotal pagesです。各プロパティは、本に関する特定の情報を保持するように設計されており、アプリケーション内で本のデータを管理し操作するのが容易になります。

モデルを作成したので、次はデータベースコンテキストを作成しましょう。

データベースコンテキストの作成方法

データベースコンテキストは、データベースとのセッションを表すクラスです。データベースとのインタラクションとデータベース操作の実行がその役割です。私たちのアプリケーションでは、Entity Framework Coreを使用してデータベースとやり取りします。

必要なパッケージのインストール

データベースコンテキストを作成する前に、以下のパッケージをインストールする必要があります:

これらのパッケージは、以下のコマンドを使用してインストールできます:

dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package FluentValidation.DependencyInjectionExtensions

パッケージのインストールを確認する

パッケージがインストールされていることを確認するには、プロジェクトのルートディレクトリにあるbookapi-minimal.csprojファイルを開いてください。インストールされたパッケージが以下のようにリストされているはずです:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <RootNamespace>bookapi_minimal</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
   <PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.2" />
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.6" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
  </ItemGroup>

</Project>

これでパッケージが正常にインストールされたことが確認できます。

それでは、データベースコンテキストを作成しましょう。

AppContextフォルダに、ApplicationContext.csという名前の新しいファイルを作成し、以下のコードを追加します:

// AppContext/ApplicationContext.cs

using bookapi_minimal.Models;
using Microsoft.EntityFrameworkCore;

namespace bookapi_minimal.AppContext
{

    public class ApplicationContext(DbContextOptions<ApplicationContext> options) : DbContext(options)
    {

        // デフォルトのスキーマをデータベースコンテキストに設定
        private const string DefaultSchema = "bookapi";


       // データベース内の本のコレクションを表すDbSet
        public DbSet<BookModel> Books { get; set; }

        // データベースコンテキストを設定するコンストラクタ

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.HasDefaultSchema(DefaultSchema);

            modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly);

            modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly);

        }

    }
}

上記のコードを分解してみましょう:

  • ApplicationContextという名前のクラスを定義し、DbContextから継承します。DbContextクラスはEntity Framework Coreの一部であり、データベースとのセッションを表します。

  • コンストラクタはDbContextOptions<ApplicationContext>のインスタンスを受け取ります。このコンストラクタはデータベースコンテキストオプションを設定するために使用されます。

  • Booksという名前のプロパティをDbSet<BookModel>型で定義します。このプロパティはデータベース内の本のコレクションを表します。

  • データベーススキーマを設定し、アプリケーションで定義された設定を適用するために、OnModelCreatingメソッドをオーバーライドします。

データベースコンテキストを作成したので、拡張メソッドを作成し、依存性注入コンテナにデータベースコンテキストを登録しましょう。

拡張メソッドを作成する

拡張メソッドを作成する前に、ASP.NET Coreの文脈で拡張メソッドが何であるかを理解しましょう。

拡張メソッドは、元のタイプを変更せずに既存のタイプに新しい機能を追加する静的メソッドです。ASP.NET Coreでは、拡張メソッドは通常、依存性注入コンテナにサービスを登録するために使用されるIServiceCollectionインターフェースの機能を拡張するために使用されます。

サービスは、データベースアクセス、ロギング、設定などの機能をアプリケーションに提供するコンポーネントです。IServiceCollectionインターフェース用の拡張メソッドを作成することで、依存性注入コンテナにサービスを登録するプロセスを簡略化できます。

すべてをProgram.csファイルに入れる代わりに、依存性注入コンテナにサービスを登録するための拡張メソッドを作成します。これにより、コードをクリーンで整理された状態に保つことができます。

Extensionsフォルダ内に、ServiceExtensions.csという名前の新しいファイルを作成し、以下のコードを追加します:

using System.Reflection;
using bookapi_minimal.AppContext;
using FluentValidation;
using Microsoft.EntityFrameworkCore;

namespace bookapi_minimal.Extensions
{
    public static class ServiceExtensions
    {
        public static void AddApplicationServices(this IHostApplicationBuilder builder)
        {
            if (builder == null) throw new ArgumentNullException(nameof(builder));
            if (builder.Configuration == null) throw new ArgumentNullException(nameof(builder.Configuration));

            // データベースコンテキストの追加
            builder.Services.AddDbContext<ApplicationContext>(configure =>
            {
                configure.UseSqlServer(builder.Configuration.GetConnectionString("sqlConnection"));
            });

            // 現在のアセンブリからのバリデータの追加
            builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
        }
    }
}

上記のコードを分解してみましょう:

  • staticクラスServiceExtensionsを定義し、その中にAddApplicationServicesという名前の拡張メソッドを含めます。このメソッドは、アプリケーションのリクエスト処理パイプラインを構成するために使用されるIHostApplicationBuilderインターフェースを拡張します。

  • AddApplicationServicesメソッドは、IHostApplicationBuilderのインスタンスをパラメータとして受け取ります。このパラメータは、アプリケーションの設定およびサービスにアクセスするために使用されます。

  • 私たちは、依存性注入コンテナーにApplicationContextを追加し、データベースプロバイダーとしてSQL Serverを使用するように設定します。接続文字列は、GetConnectionStringメソッドを使用してappsettings.jsonファイルから取得します。

  • 私たちは、AddValidatorsFromAssemblyメソッドを使用して現在のassemblyからvalidatorsを追加します。このメソッドは、現在のアセンブリ内でIValidatorインターフェースを実装するクラスをスキャンし、依存性注入コンテナーに登録します。

次に、接続文字列をappsettings.jsonファイルに追加する必要があります。以下のコードをあなたのappsettings.jsonファイルに追加してください:

{ 
     "ConnectionStrings": {
    "sqlConnection": "Server=localhost\\SQLEXPRESS02;Database=BookAPIMinimalAPI;Integrated Security=true;TrustServerCertificate=true;"
  }
  }

your_passwordを実際のSQL Serverのパスワードに置き換えてください。

あなたのappsettings.jsonファイルは次のようになるはずです:


{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "sqlConnection": "Server=localhost\\SQLEXPRESS02;Database=BookAPIMinimalAPI;Integrated Security=true;TrustServerCertificate=true;"
  },
  "AllowedHosts": "*"
}

おめでとうございます!データベースコンテキスト、拡張メソッド、およびアプリケーションの接続文字列の作成に成功しました。次のセクションでは、契約を ایجادします。

契約の作成方法

契約は、クライアントとサーバー間で交換されるデータの構造を定義するデータ転送オブジェクト(DTO)です。私たちのアプリケーションでは、APIエンドポイントによって送信および受信されるデータを表すために契約を作成します。

以下は、作成する契約です:

  • CreateBookRequest: 新しい本を作成する際に送信されるデータを表します。

  • UpdateBookRequest: 既存の本を更新する際に送信されるデータを表します。

  • BookResponse: 本を取得する際に返されるデータを表します。

  • ErrorResponse: 例外が発生した際に返されるエラーレスポンスを表します。

  • ApiResponse: APIによって返されるレスポンスを表します。

Contractsフォルダーに、CreateBookRequestという新しいファイルを作成し、以下のコードを追加します:

// Contracts/CreateBookRequest.cs

namespace bookapi_minimal.Contracts
{

    public record CreateBookRequest
    { 

        public string Title { get; init; }
        public string Author { get; init; }
        public string Description { get; init; }
        public string Category { get; init; }
        public string Language { get; init; }
        public int TotalPages { get; init; }
    }
}

Contractsフォルダーに、UpdateBookRequestという新しいファイルを作成し、以下のコードを追加します:


// Contracts/UpdateBookRequest.cs

namespace bookapi_minimal.Contracts
{

    public record UpdateBookRequest
    {
       public string Title { get; set; }
        public string Author { get; set; }
        public string Description { get; set; }
        public string Category { get; set; }
        public string Language { get; set; }
        public int TotalPages { get; set; }

    }
}

Contractsフォルダに、BookResponseという名前の新しいファイルを作成し、以下のコードを追加します:

// Contracts/BookResponse.cs
namespace bookapi_minimal.Contracts
{

    public record BookResponse
    {
        public Guid Id { get; set; }
        public string Title { get; set; }
        public string Author { get; set; }
        public string Description { get; set; }
        public string Category { get; set; }
        public string Language { get; set; }
        public int TotalPages { get; set; }
    }
}

Contractsフォルダに、ErrorResponseという名前の新しいファイルを作成し、以下のコードを追加します:



// Contracts/ErrorResponse.cs
namespace bookapi_minimal.Contracts
{

        public record ErrorResponse
    {
        public string Title { get; set; }
        public int StatusCode { get; set; }
        public string Message { get; set; }

    }

}

Contractsフォルダに、ApiResponseという名前の新しいファイルを作成し、以下のコードを追加します:

// Contracts/ApiResponse.cs
namespace bookapi_minimal.Contracts
{

    public class ApiResponse<T>
    {
        public T Data { get; set; }
        public string Message { get; set; }

        public ApiResponse(T data, string message)
        {
            Data = data;
            Message = message;
        }
    }
}

これらの契約は、クライアントとサーバー間で交換されるデータの構造を定義するのに役立ち、アプリケーション内でデータを扱うことを容易にします。

次のセクションでは、アプリケーションのビジネスロジックを実装するためのサービスを作成します。

サービスの追加方法

サービスは、アプリケーションに機能を提供するコンポーネントです。私たちのアプリケーションでは、アプリケーションのビジネスロジックを実装するためのサービスを作成します。書籍のCRUD操作を処理し、書籍データを検証し、例外を処理するサービスを作成します。

ASP.NET Coreでは、サービスは依存関係注入コンテナーに登録され、コントローラーやエンドポイントなどの他のコンポーネントに注入できますが、これは最小限のAPIなので、サービスを直接エンドポイントに注入します。

サービスのインターフェースを作成しましょう。

 // Interfaces/IBookService.cs



using bookapi_minimal.Contracts;

namespace bookapi_minimal.Interfaces
{
      public interface IBookService
    {
        Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest);
        Task<BookResponse> GetBookByIdAsync(Guid id);
        Task<IEnumerable<BookResponse>> GetBooksAsync();
        Task<BookResponse> UpdateBookAsync(Guid id,  UpdateBookRequest  updateBookRequest);
        Task<bool> DeleteBookAsync(Guid id);
    }
}

上記のコードを分解しましょう: 私たちは、本のCRUD操作を処理するメソッドを含むIBookServiceという名前のインターフェースを定義しています。このインターフェースは以下のメソッドを定義しています:

  • AddBookAsync: 新しい本をデータベースに追加します。

  • GetBookByIdAsync: IDによって本を取得します。

  • GetBooksAsync: データベースからすべての本を取得します。

  • UpdateBookAsync: 既存の本を更新します。

私たちは earlier に作成したContractsフォルダ内の契約を使用しています。IBookServiceインターフェースは、サービスクラスによって実装されるメソッドの構造を定義しています。これにより、インターフェースと実装を分離し、コードのメンテナンスとテストが容易になります。

インターフェースを作成したので、次にインターフェースを実装するサービスクラスを作成しましょう。

Book Serviceの実装方法

このサービスはIBookServiceインターフェースを実装し、アプリケーションのビジネスロジックを提供します。Servicesフォルダ内に新しいファイルを作成し、BookService.csという名前を付けます。初期のファイルは以下のようになります:


// Services/BookService.cs

namespace bookapi_minimal.Services
{
    public class BookService
    {

    }
}

まず行うべきことは、インターフェースをBookServiceクラスに追加することです。BookServiceクラスを更新して、IBookServiceインターフェースを実装するようにします:



// Services/BookService.cs



using bookapi_minimal.Interfaces;

namespace bookapi_minimal.Services
{
    public class BookService:IBookService
    {

    }
}

これを行うと、VS Codeにエラーが表示されることがあります。これは、インターフェースのメソッドを実装していないためです。では、BookServiceクラスでメソッドを実装しましょう。

VS Codeでは、Ctrl + .ショートカットを使用してインターフェースのメソッドを実装できます。すると、以下のコードが生成されます:


using bookapi_minimal.Contracts;
using bookapi_minimal.Interfaces;

namespace bookapi_minimal.Services
{
     // 書籍を管理するサービスクラス
   public class BookService : IBookService
   {
       // データベースに新しい書籍を追加するメソッド
       public Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest)
       {
           throw new NotImplementedException();
       }

      // データベースから書籍を削除するメソッド
       public Task<bool> DeleteBookAsync(Guid id)
       {
           throw new NotImplementedException();
       }

       // データベースからIDで書籍を取得するメソッド

       public Task<BookResponse> GetBookByIdAsync(Guid id)
       {
           throw new NotImplementedException();
       }

      // データベースからすべての書籍を取得するメソッド
       public Task<IEnumerable<BookResponse>> GetBooksAsync()
       {
           throw new NotImplementedException();
       }

       // データベース内の書籍を更新するメソッド
       public Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest updateBookRequest)
       {
           throw new NotImplementedException();
       }
   }
}

今、インターフェースのメソッドがBookServiceクラスで実装されていることがわかります。次のセクションで各メソッドのビジネスロジックを実装します。

それをする前に、BookServiceクラスに必要な依存関係を追加しましょう。ApplicationContextILoggerの依存関係をBookServiceクラスに注入する必要があります。ApplicationContextはデータベースとやり取りするために使用され、ILoggerはログ記録に使用されます。

依存関係を注入するために、BookServiceクラスを以下のように更新します:


// Services/BookService.cs

// ...
 private readonly ApplicationContext _context; // データベースコンテキスト
  private readonly ILogger<BookService> _logger; // 情報とエラーを記録するためのロガー

//..

依存関係を追加したので、BookServiceコンストラクターを更新して依存関係を受け入れる必要があります。BookServiceコンストラクターを以下のように更新します:


// Services/BookService.cs

// ...

  // データベースコンテキストとロガーを初期化するコンストラクター
 public BookService(ApplicationContext context, ILogger<BookService> logger)
 {
            _context = context;
            _logger = logger;
}

// ...

依存関係を追加し、コンストラクターを更新したので、BookServiceクラスの各メソッドのビジネスロジックを実装することができます。

「CREATE、READ、UPDATE、DELETE操作のロジックをBookServiceクラスで作成しましょう。

AddBookAsyncメソッドの実装方法

前述したように、AddBookAsyncメソッドを使用して新しい本をデータベースに追加します。このメソッドでは、新しい本のエンティティを作成し、CreateBookRequestオブジェクトからデータを本のエンティティにマッピングし、本のエンティティをデータベースに保存します。また、本のエンティティをBookResponseオブジェクトとして返します。

BookServiceクラスのAddBookAsyncメソッドを以下のように更新します:

// Services/BookService.cs

// ...
 /// <summary>
        /// 新しい本を追加
        /// </summary>
        /// <param name="createBookRequest">追加する本のリクエスト</param>
        /// <returns>作成された本の詳細</returns>
        public async Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest)
        {
            try
            {
                var book = new BookModel
                {
                    Title = createBookRequest.Title,
                    Author = createBookRequest.Author,
                    Description = createBookRequest.Description,
                    Category = createBookRequest.Category,
                    Language = createBookRequest.Language,
                    TotalPages = createBookRequest.TotalPages
                };

                // 本をデータベースに追加
                _context.Books.Add(book);
                await _context.SaveChangesAsync();
                _logger.LogInformation("Book added successfully.");

                // 作成された本の詳細を返す
                return new BookResponse
                {
                    Id = book.Id,
                    Title = book.Title,
                    Author = book.Author,
                    Description = book.Description,
                    Category = book.Category,
                    Language = book.Language,
                    TotalPages = book.TotalPages
                };
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error adding book: {ex.Message}");
                throw;
            }
        }
// ...

このコードでは、CreateBookRequestオブジェクトから新しい書籍エンティティを作成し、CreateBookRequestオブジェクトから書籍エンティティへのデータマッピングを行い、書籍エンティティをデータベースに保存し、書籍エンティティをBookResponseオブジェクトとして返しています。

また、ILogger依存関係を使用して情報とエラーをログ記録しています。プロセス中に例外が発生した場合、エラーメッセージをログ記録し、例外を再スローします。

これでAddBookAsyncメソッドの実装が完了したので、次にGetBookByIdAsyncメソッドを実装しましょう。

GetBookByIdAsyncメソッドの実装方法

GetBookByIdAsyncメソッドは、データベースから指定されたIDを持つ書籍を取得するために使用されます。このメソッドでは、指定されたIDを持つ書籍をデータベースでクエリし、書籍エンティティをBookResponseオブジェクトにマッピングし、BookResponseオブジェクトを返します。

BookServiceクラスのGetBookByIdAsyncメソッドを以下のように更新してください:


// Services/BookService.cs

//... 

    /// <summary>
        /// IDで本を取得する
        /// </summary>
        /// <param name="id">本のID</param>
        /// <returns>本の詳細</returns>
        public async Task<BookResponse>  GetBookByIdAsync(Guid id)
        {
            try
            {
                // IDで本を検索する
                var book = await _context.Books.FindAsync(id);
                if (book == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return null;
                }

                // 本の詳細を返す
                return new BookResponse
                {
                    Id = book.Id,
                    Title = book.Title,
                    Author = book.Author,
                    Description = book.Description,
                    Category = book.Category,
                    Language = book.Language,
                    TotalPages = book.TotalPages
                };
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error retrieving book: {ex.Message}");
                throw;
            }
        }

//...

このコードでは、指定されたIDの本をデータベースでクエリし、本のエンティティをBookResponseオブジェクトにマッピングし、BookResponseオブジェクトを返しています。また、ILogger依存関係を使用して情報とエラーをログ記録しています。

指定されたIDの本が見つからない場合、警告メッセージをログ記録し、nullを返します。プロセス中に例外が発生した場合、エラーメッセージをログ記録し、例外を再スローします。

これでGetBookByIdAsyncメソッドを実装したので、次にGetBooksAsyncメソッドを実装しましょう。

GetBooksAsyncメソッドの実装方法

GetBooksAsyncメソッドは、データベースからすべての本を取得するために使用されます。このメソッドでは、データベースにすべての本を問い合わせ、各本エンティティをBookResponseオブジェクトにマッピングし、BookResponseオブジェクトのリストを返します。

BookServiceクラスのGetBooksAsyncメソッドを以下のように更新します:



// Services/BookService.cs

//... 


  /// <summary>
        /// すべての本を取得
        /// </summary>
        /// <returns>すべての本のリスト</returns>
        public async Task<IEnumerable<BookResponse>> GetBooksAsync()
        {
            try
            {
                // データベースからすべての本を取得
                var books = await _context.Books.ToListAsync();

                // すべての本の詳細を返す
                return books.Select(book => new BookResponse
                {
                    Id = book.Id,
                    Title = book.Title,
                    Author = book.Author,
                    Description = book.Description,
                    Category = book.Category,
                    Language = book.Language,
                    TotalPages = book.TotalPages
                });
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error retrieving books: {ex.Message}");
                throw;
            }
        }
//...

ここでは、データベースにすべての本を問い合わせ、各本エンティティをBookResponseオブジェクトにマッピングし、BookResponseオブジェクトのリストを返しています。また、ILogger依存関係を使用して情報とエラーをログ記録しています。プロセス中に例外が発生した場合、エラーメッセージをログ記録し、例外を再スローします。

GetBooksAsyncメソッドを実装したので、次にUpdateBookAsyncメソッドを実装しましょう。

UpdateBookAsyncメソッドの実装方法

UpdateBookAsyncメソッドは、データベース内の既存の本を更新するために使用されます。このメソッドでは、指定されたIDを持つ本をデータベースでクエリし、UpdateBookRequestオブジェクトからのデータで本エンティティを更新し、更新された本エンティティをデータベースに保存し、更新された本エンティティをBookResponseオブジェクトとして返します。

BookServiceクラスのUpdateBookAsyncメソッドを以下のように更新してください:

// Services/BookService.cs
 //...
 /// <summary>
        /// 既存の本を更新する
        /// </summary>
        /// <param name="id">更新する本のID</param>
        /// <param name="book">更新された本のモデル</param>
        /// <returns>更新された本の詳細</returns>
        public async Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest book)
        {
            try
            {
                // 既存の本をIDで検索する
                var existingBook = await _context.Books.FindAsync(id);
                if (existingBook == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return null;
                }

                // 本の詳細を更新する
                existingBook.Title = book.Title;
                existingBook.Author = book.Author;
                existingBook.Description = book.Description;
                existingBook.Category = book.Category;
                existingBook.Language = book.Language;
                existingBook.TotalPages = book.TotalPages;

                // 変更をデータベースに保存する
                await _context.SaveChangesAsync();
                _logger.LogInformation("Book updated successfully.");

                // 更新された本の詳細を返す
                return new BookResponse
                {
                    Id = existingBook.Id,
                    Title = existingBook.Title,
                    Author = existingBook.Author,
                    Description = existingBook.Description,
                    Category = existingBook.Category,
                    Language = existingBook.Language,
                    TotalPages = existingBook.TotalPages
                };
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error updating book: {ex.Message}");
                throw;
            }
        }
//...

ここでは、指定されたIDの本をデータベースでクエリし、UpdateBookRequestオブジェクトからのデータで本エンティティを更新し、更新された本エンティティをデータベースに保存し、BookResponseオブジェクトとして更新された本エンティティを返しています。また、ILogger依存関係を使用して情報とエラーをログ記録しています。

指定されたIDの本が見つからない場合、警告メッセージをログ記録し、nullを返します。プロセス中に例外が発生した場合、エラーメッセージをログ記録し、例外を再スローします。

これでUpdateBookAsyncメソッドを実装したので、次にDeleteBookAsyncメソッドを実装しましょう。

DeleteBookAsyncメソッドの実装方法

DeleteBookAsyncメソッドは、データベースから既存の本を削除するために使用されます。このメソッドでは、指定されたIDの本をデータベースでクエリし、本エンティティをデータベースから削除し、本が正常に削除されたかどうかを示すブール値を返します。

BookServiceクラスのDeleteBookAsyncメソッドを以下のように更新します:

// Services/BookService.cs

 //...


/// <summary>
        /// IDで指定された本を削除する
        /// </summary>
        /// <param name="id">削除する本のID</param>
        /// <returns>本が削除された場合はtrue、それ以外はfalse</returns>
        public async Task<bool> DeleteBookAsync(Guid id)
        {
            try
            {
                // IDで本を検索する
                var book = await _context.Books.FindAsync(id);
                if (book == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return false;
                }

                // データベースから本を削除する
                _context.Books.Remove(book);
                await _context.SaveChangesAsync();
                _logger.LogInformation($"Book with ID {id} deleted successfully.");
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error deleting book: {ex.Message}");
                throw;
            }
        }
//...

このコードでは、指定されたIDを持つ本をデータベースで検索し、データベースから本のエンティティを削除し、本が正常に削除されたかどうかを示すブール値を返しています。また、ILogger依存関係を使用して情報とエラーをログに記録しています。

指定されたIDの本が見つからない場合、警告メッセージをログに記録し、falseを返します。処理中に例外が発生した場合、エラーメッセージをログに記録し、例外を再スローします。

現在、BookServiceクラスのAddBookAsyncGetBookByIdAsyncGetBooksAsyncUpdateBookAsync、およびDeleteBookAsyncメソッドに対する業務ロジックを正常に実装しました。これらのメソッドは、本のCRUD操作を行い、本のデータを検証し、例外を処理します。この時点で、BookServiceクラスは以下のようになっているはずです:



using bookapi_minimal.AppContext;
using bookapi_minimal.Contracts;
using bookapi_minimal.Interfaces;
using bookapi_minimal.Models;
using Microsoft.EntityFrameworkCore;

namespace bookapi_minimal.Services
{
    public class BookService : IBookService
    {
          private readonly ApplicationContext _context; // データベースコンテキスト
        private readonly ILogger<BookService> _logger; // 情報とエラーを記録するためのロガー
          // データベースコンテキストとロガーを初期化するコンストラクタ
        public BookService(ApplicationContext context, ILogger<BookService> logger)
        {
            _context = context;
            _logger = logger;
        }

           /// 新しい本を追加
        /// </summary>
        /// <param name="createBookRequest">追加する本のリクエスト</param>
        /// <returns>作成された本の詳細</returns>
        public async Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest)
        {
            try
            {
                var book = new BookModel
                {
                    Title = createBookRequest.Title,
                    Author = createBookRequest.Author,
                    Description = createBookRequest.Description,
                    Category = createBookRequest.Category,
                    Language = createBookRequest.Language,
                    TotalPages = createBookRequest.TotalPages
                };

                // 本をデータベースに追加
                _context.Books.Add(book);
                await _context.SaveChangesAsync();
                _logger.LogInformation("Book added successfully.");

                // 作成された本の詳細を返す
                return new BookResponse
                {
                    Id = book.Id,
                    Title = book.Title,
                    Author = book.Author,
                    Description = book.Description,
                    Category = book.Category,
                    Language = book.Language,
                    TotalPages = book.TotalPages
                };
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error adding book: {ex.Message}");
                throw;
            }
        }

          /// <summary>
        /// IDで本を取得
        /// </summary>
        /// <param name="id">本のID</param>
        /// <returns>本の詳細</returns>
        public async Task<BookResponse>  GetBookByIdAsync(Guid id)
        {
            try
            {
                // IDで本を検索
                var book = await _context.Books.FindAsync(id);
                if (book == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return null;
                }

                // 本の詳細を返す
                return new BookResponse
                {
                    Id = book.Id,
                    Title = book.Title,
                    Author = book.Author,
                    Description = book.Description,
                    Category = book.Category,
                    Language = book.Language,
                    TotalPages = book.TotalPages
                };
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error retrieving book: {ex.Message}");
                throw;
            }
        }



  /// <summary>
        /// すべての本を取得
        /// </summary>
        /// <returns>すべての本のリスト</returns>
        public async Task<IEnumerable<BookResponse>> GetBooksAsync()
        {
            try
            {
                // データベースからすべての本を取得
                var books = await _context.Books.ToListAsync();

                // すべての本の詳細を返す
                return books.Select(book => new BookResponse
                {
                    Id = book.Id,
                    Title = book.Title,
                    Author = book.Author,
                    Description = book.Description,
                    Category = book.Category,
                    Language = book.Language,
                    TotalPages = book.TotalPages
                });
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error retrieving books: {ex.Message}");
                throw;
            }
        }


         /// <summary>
        /// 既存の本を更新
        /// </summary>
        /// <param name="id">更新する本のID</param>
        /// <param name="book">更新された本のモデル</param>
        /// <returns>更新された本の詳細</returns>
        public async Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest book)
        {
            try
            {
                // IDで既存の本を検索
                var existingBook = await _context.Books.FindAsync(id);
                if (existingBook == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return null;
                }

                // 本の詳細を更新
                existingBook.Title = book.Title;
                existingBook.Author = book.Author;
                existingBook.Description = book.Description;
                existingBook.Category = book.Category;
                existingBook.Language = book.Language;
                existingBook.TotalPages = book.TotalPages;

                // データベースへの変更を保存
                await _context.SaveChangesAsync();
                _logger.LogInformation("Book updated successfully.");

                // 更新された本の詳細を返す
                return new BookResponse
                {
                    Id = existingBook.Id,
                    Title = existingBook.Title,
                    Author = existingBook.Author,
                    Description = existingBook.Description,
                    Category = existingBook.Category,
                    Language = existingBook.Language,
                    TotalPages = existingBook.TotalPages
                };
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error updating book: {ex.Message}");
                throw;
            }
        }



        /// <summary>
        /// IDで本を削除
        /// </summary>
        /// <param name="id">削除する本のID</param>
        /// <returns>本が削除された場合はtrue、それ以外はfalse</returns>
        public async Task<bool> DeleteBookAsync(Guid id)
        {
            try
            {
                // IDで本を検索
                var book = await _context.Books.FindAsync(id);
                if (book == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return false;
                }

                // 本をデータベースから削除
                _context.Books.Remove(book);
                await _context.SaveChangesAsync();
                _logger.LogInformation($"Book with ID {id} deleted successfully.");
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error deleting book: {ex.Message}");
                throw;
            }
        }

    }
}

おめでとうございます!AddBookAsyncGetBookByIdAsyncGetBooksAsyncUpdateBookAsync、およびDeleteBookAsyncメソッドのビジネスロジックをBookServiceクラスに正常に実装しました。

やるべきことが1つあります:サービスを拡張メソッドに登録する必要があります。それを行いましょう。

ServiceExtensions.csファイルに、次のコードを追加してください:


// Extensions/ServiceExtensions.cs

//..

 builder.Services.AddScoped<IBookService, BookService>();
//...

これにより、BookServiceクラスがスコープされたサービスとして登録されます。つまり、サービスはリクエストごとに1回作成され、リクエストが完了した後に破棄されます。

サービスが動作しているので、例外クラスの作成に進みましょう。

例外の作成方法

例外を適切に処理することは、アプリケーションの安定性と信頼性を確保するために重要です。ASP.NET Coreのコンテキストでは、主に2つの種類の例外があります:

  • システム例外:これらは.NETランタイムまたは基礎システムによってスローされる例外です。

  • アプリケーション例外:これらは、特定のエラーや条件を処理するためにアプリケーションコードによってスローされる例外です。

ASP.NET Core with .NET 8では、グローバル例外処理と呼ばれる新しい機能が導入されました。この機能により、アプリケーション全体で例外をグローバルに処理することができ、エラーの管理が容易になり、一貫したユーザーエクスペリエンスを提供することができます。

私たちのアプリケーションでは、特定のエラーや条件を処理するためのカスタム例外クラスを作成します。また、グローバル例外処理機能を活用して、アプリケーション全体で例外を管理し、エラーハンドリングの一貫したアプローチを保証します。

以下の例外クラスを作成する予定です:

  • NoBookFoundException: 指定されたIDの書籍が見つからない場合に投げられます。

  • BookDoesNotExistException: 指定されたIDの書籍が存在しない場合に投げられます。

  • GlobalExceptionHandler: アプリケーション全体で例外を処理します。

Exceptionsフォルダ内に、NoBookFoundException.csという新しいファイルを作成し、以下のコードを追加します:


// Exceptions/NoBookFoundException.cs

namespace bookapi_minimal.Exceptions
{

    public class NoBookFoundException : Exception
    {

        public NoBookFoundException() : base("No books found")
        {}
    }
}

このコードでは、NoBookFoundExceptionという名前のカスタム例外クラスを作成しており、これはExceptionクラスを継承しています。NoBookFoundExceptionクラスは、データベースに本が見つからないシナリオを処理するために使用されます。また、例外に対するカスタムエラーメッセージも提供しています。

Exceptionsフォルダ内に、BookDoesNotExistException.csという名前の新しいファイルを作成し、次のコードを追加します:

namespace bookapi_minimal.Exceptions
{
     public class BookDoesNotExistException : Exception
    {
        private int id { get; set; }

        public BookDoesNotExistException(int id) : base($"Book with id {id} does not exist")
        {
            this.id = id;
        } 

    }
}

このコードでは、BookDoesNotExistExceptionという名前のカスタム例外クラスを作成しており、これはExceptionクラスを継承しています。BookDoesNotExistExceptionクラスは、指定されたIDを持つ本がデータベースに存在しないシナリオを処理するために使用されます。また、例外に対するカスタムエラーメッセージも提供しています。

Exceptionsフォルダ内に、GlobalExceptionHandler.csという名前の新しいファイルを作成し、次のコードを追加します:

// Exceptions/GlobalExceptionHandler.cs

using System.Net;
using bookapi_minimal.Contracts;
using Microsoft.AspNetCore.Diagnostics;

namespace bookapi_minimal.Exceptions
{

   // グローバル例外処理クラスがIExceptionHandlerを実装
    public class GlobalExceptionHandler : IExceptionHandler
    {
        private readonly ILogger<GlobalExceptionHandler> _logger;

        // ロガーを初期化するコンストラクタ
        public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
        {
            _logger = logger;
        }

        // 非同期に例外を処理するメソッド
        public async ValueTask<bool> TryHandleAsync(
            HttpContext httpContext,
            Exception exception,
            CancellationToken cancellationToken)
        {
            // 例外の詳細をログに記録
            _logger.LogError(exception, "An error occurred while processing your request");

            var errorResponse = new ErrorResponse
            {
                Message = exception.Message,
                Title = exception.GetType().Name
            };

            // 例外の種類に基づいてステータスコードを決定
            switch (exception)
            {
                case BadHttpRequestException:
                    errorResponse.StatusCode = (int)HttpStatusCode.BadRequest;
                    break;

                case NoBookFoundException:
                case BookDoesNotExistException:
                    errorResponse.StatusCode = (int)HttpStatusCode.NotFound;
                    break;

                default:
                    errorResponse.StatusCode = (int)HttpStatusCode.InternalServerError;
                    break;
            }

            // レスポンスのステータスコードを設定
            httpContext.Response.StatusCode = errorResponse.StatusCode;

            // エラーレスポンスをJSONとして書き込む
            await httpContext.Response.WriteAsJsonAsync(errorResponse, cancellationToken);

            // 例外が処理されたことを示すためにtrueを返す
            return true;
        }
    }
}

上記のコードを分解してみましょう:

  • 私たちはGlobalExceptionHandlerという名前のクラスを定義し、IExceptionHandlerインターフェースを実装します。IExceptionHandlerインターフェースは、アプリケーション全体で例外をグローバルに処理するために使用されます。

  • GlobalExceptionHandlerクラスには、ILogger<GlobalExceptionHandler>依存関係を初期化するコンストラクタが含まれています。ILoggerは、情報とエラーのログ記録に使用されます。

  • TryHandleAsync メソッドは、例外を非同期的に処理するために使用されます。このメソッドは、HttpContextException、および CancellationToken をパラメーターとして受け取ります。

  • 私たちは、ILogger 依存関係を使用して例外の詳細をログ記録します。

  • API によって返されるエラー応答を表すために、ErrorResponse オブジェクトを作成します。ErrorResponse オブジェクトには、エラーメッセージ、タイトル、およびステータスコードが含まれています。

  • 例外のタイプに基づいてステータスコードを決定します。例外が BadHttpRequestException の場合、ステータスコードを BadRequest に設定します。例外が NoBookFoundException または BookDoesNotExistException の場合、ステータスコードを NotFound に設定します。それ以外の場合は、ステータスコードを InternalServerError に設定します。

  • 応答ステータスコードは、httpContext.Response.StatusCodeプロパティを使用して設定します。

  • エラーレスポンスは、httpContext.Response.WriteAsJsonAsyncメソッドを使用してJSONとして書き込みます。

  • 例外が正常に処理されたことを示すために、trueを返します。

例外クラスを作成したので、依存関係注入コンテナーにGlobalExceptionHandlerを登録しましょう。依存関係注入コンテナーにサービスを登録するための拡張メソッドを作成したので、GlobalExceptionHandlerServiceExtensionsクラスに追加します。

Extensionsフォルダ内のServiceExtensionsクラスを以下のように更新します:


// Extensions/ServiceExtensions.cs
//...
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();

builder.Services.AddProblemDetails();

//...

AddExceptionHandlerメソッドは、依存関係注入コンテナーにGlobalExceptionHandlerを登録します。AddProblemDetailsメソッドは、依存関係注入コンテナーにProblemDetailsクラスを登録します。

依存関係注入コンテナーにGlobalExceptionHandlerを登録したので、アプリケーション全体で例外をグローバルに処理するために使用できます。次のセクションでは、書籍データとやり取りするAPIエンドポイントを作成します。

APIエンドポイントの作成方法

ASP.NET CoreのミニマルAPIの文脈では、エンドポイントを設定する方法はいくつかあります。

それらを直接Program.csファイルで定義することもできます。しかし、プロジェクトが成長し、より多くのエンドポイントや機能を追加する必要がある場合は、コードをよりよく整理することが役立ちます。これを実現する一つの方法は、エンドポイントをすべて処理する別のクラスを作成することです。

上述したように、ミニマルAPIは従来のASP.NET Coreアプリケーションのようにコントローラーやビューを使用しません。代わりに、MapGetMapPostMapPutMapDeleteなどのメソッドを使用して、APIエンドポイントのHTTPメソッドとルートを定義します。

始めるには、Endpointsフォルダーに移動し、BookEndpoints.csという新しいファイルを作成します。次のコードをファイルに追加します:


// Endpoints/BookEndpoints.cs



namespace bookapi_minimal.Endpoints
{
     public static class BookEndPoint
    {
        public static IEndpointRouteBuilder MapBookEndPoint(this IEndpointRouteBuilder app)
        {


            return app;
        }
    }
}

BookEndpoints クラスには、IEndpointRouteBuilder オブジェクトを返す MapBookEndPoint メソッドが含まれています。IEndpointRouteBuilder オブジェクトは、API エンドポイントのHTTPメソッドとルートを定義するために使用されます。次のセクションでは、作成読み取り更新、および 削除 のためのAPIエンドポイントを定義します。

AddBookAsync エンドポイントの作成方法

このセクションでは、AddBookAsync エンドポイントを作成します。このエンドポイントは、JSONペイロードとしてBookオブジェクトを受け取り、データベースに追加します。このエンドポイントのHTTPメソッドとルートを定義するために、MapPost メソッドを使用します。

BookEndpoints クラスに以下のコードを追加します:


// Endpoints/BookEndpoints.cs


//...
   // 新しい本を追加するエンドポイント
      app.MapPost("/books", async (CreateBookRequest createBookRequest, IBookService bookService) =>
        {
        var result = await bookService.AddBookAsync(createBookRequest);
        return Results.Created($"/books/{result.Id}", result); 
        });


//...
  • ルート定義: MapPostメソッドは、エンドポイントのルートを/booksとして定義します。

  • リクエストモデル: エンドポイントは、JSONペイロードとしてCreateBookRequestオブジェクトを受け取ります。CreateBookRequestオブジェクトには、新しい本を作成するために必要なデータが含まれています。

  • レスポンスモデル: エンドポイントは、JSONペイロードとしてBookオブジェクトを返します。Bookオブジェクトには、新しく作成された本のデータが含まれています。

  • 戻り値: エンドポイントは、Created結果を返します。Created結果には、新しく作成された本の場所とBookオブジェクトが含まれています。

GetBookAsync本エンドポイントの作成方法

このセクションでは、GetBookAsyncエンドポイントを作成します。このエンドポイントは、クエリパラメータとして本のIDを受け取り、指定されたIDの本を返します。このエンドポイントのHTTPメソッドとルートを定義するために、MapGetメソッドを使用します。

BookEndpointsクラスに以下のコードを追加します:


// Endpoints/BookEndpoints.cs

// ...
    // 全ての本を取得するエンドポイント
    app.MapGet("/books", async (IBookService bookService) =>
     {
    var result = await bookService.GetBooksAsync();
    return Results.Ok(result);
});


//...
  • ルート定義: MapGetメソッドは、エンドポイントのルートを/booksとして定義します。

  • リクエストモデル: エンドポイントは、JSONペイロードとしてBookオブジェクトを受け取ります。 Bookオブジェクトには、新しい本を作成するために必要なデータが含まれています。

  • レスポンスモデル: エンドポイントは、JSONペイロードとしてBookオブジェクトを返します。 Bookオブジェクトには、新しく作成された本のデータが含まれています。

  • 戻り値: エンドポイントは、Ok結果を返します。 Ok結果には、Bookオブジェクトが含まれています。

GetBookByIdAsync本エンドポイントの作成方法

このセクションでは、GetBookByIdAsync エンドポイントを作成します。このエンドポイントは、ルートパラメータとして書籍のIDを受け入れ、指定されたIDの書籍を返します。このエンドポイントのHTTPメソッドとルートを定義するために MapGet メソッドを使用します。


// Endpoints/BookEndpoints.cs
//...
// IDで書籍を取得するエンドポイント

  app.MapGet("/books/{id:guid}", async (Guid id, IBookService bookService) =>
  {
    var result = await bookService.GetBookByIdAsync(id);
    return result != null ? Results.Ok(result) : Results.NotFound();
});

//...
  • ルートの定義: MapGet メソッドは、以下のようにエンドポイントのルートを定義します /books/{id:guid}。パラメータ {id:guid} は、id パラメータが GUID であることを指定します。

  • リクエストモデル: エンドポイントは Book オブジェクトを JSON ペイロードとして受け入れます。 Book オブジェクトには、新しい書籍を作成するために必要なデータが含まれています。

  • レスポンスモデル: エンドポイントは Book オブジェクトを JSON ペイロードとして返します。 Book オブジェクトには、新しく作成された書籍のデータが含まれています。
  • 戻り値: エンドポイントは、本が見つかった場合にOk結果を返します。本が見つからない場合はNotFound結果が返されます。

UpdateBookAsyncエンドポイントの作成方法

このセクションでは、UpdateBookAsyncエンドポイントを作成します。このエンドポイントは、ルートパラメータとして本のIDを受け取り、JSONペイロードとしてBookオブジェクトを受け取り、指定されたIDの本を更新します。このエンドポイントのHTTPメソッドとルートを定義するために、MapPutメソッドを使用します。

BookEndpointsクラスに以下のコードを追加します:


// Endpoints/BookEndpoints.cs

//...
   // 本をIDで更新するエンドポイント
    app.MapPut("/books/{id:guid}", async (Guid id, UpdateBookRequest updateBookRequest, IBookService bookService) =>
 {
var result = await bookService.UpdateBookAsync(id, updateBookRequest);
return result != null ? Results.Ok(result) : Results.NotFound();
});

//...
  • ルート定義: MapPutメソッドは、エンドポイントのルートを/books/{id:guid}として定義します。{id:guid}パラメータは、idパラメータがGUIDであることを指定します。

  • リクエストモデル: エンドポイントは、JSONペイロードとしてBookオブジェクトを受け入れます。Bookオブジェクトには、新しい本を作成するために必要なデータが含まれています。

  • レスポンスモデル: エンドポイントは、JSONペイロードとしてBookオブジェクトを返します。Bookオブジェクトには、新しく作成された本のデータが含まれています。

  • 戻り値: エンドポイントは、本が見つかった場合にOk結果を返します。本が見つからない場合は、NotFound結果が返されます。

DeleteBookAsync本エンドポイントの作成方法

このセクションでは、DeleteBookAsyncエンドポイントを作成します。このエンドポイントは、ルートパラメータとして書籍IDを受け入れ、指定されたIDの書籍を削除します。このエンドポイントのHTTPメソッドとルートを定義するためにMapDeleteメソッドを使用します。

BookEndpointsクラスに以下のコードを追加してください:


// Endpoints/BookEndpoints.cs

//...
   // IDで書籍を削除するエンドポイント
 app.MapDelete("/books/{id:guid}", async (Guid id, IBookService bookService) =>
{
var result = await bookService.DeleteBookAsync(id);
   return result ? Results.NoContent() : Results.NotFound();
});


//...
  • ルートの定義: MapDeleteメソッドは、エンドポイントのルートを/books/{id:guid}と定義します。{id:guid}パラメータはidパラメータがGUIDであることを指定します。

  • リクエストモデル: このエンドポイントは、JSONペイロードとしてBookオブジェクトを受け入れます。Bookオブジェクトには新しい書籍を作成するために必要なデータが含まれています。

  • レスポンスモデル: このエンドポイントは、JSONペイロードとしてBookオブジェクトを返します。Bookオブジェクトには新しく作成された書籍のデータが含まれています。
  • 戻り値: エンドポイントは、本が正常に削除された場合にNoContent結果を返します。本が見つからない場合はNotFound結果が返されます。

これで本のエンドポイントのためのすべてのメソッドを定義しました。したがって、あなたのエンドポイントクラスは次のようになるはずです:

// Endpoints/BookEndpoints.cs
using bookapi_minimal.Contracts;
using bookapi_minimal.Interfaces;

namespace bookapi_minimal.Endpoints
{
     public static class BookEndPoint
    {
        public static IEndpointRouteBuilder MapBookEndPoint(this IEndpointRouteBuilder app)
        {
            // エンドポイントの定義

            // 新しい本を追加するエンドポイント
            app.MapPost("/books", async (CreateBookRequest createBookRequest, IBookService bookService) =>
            {
                var result = await bookService.AddBookAsync(createBookRequest);
                return Results.Created($"/books/{result.Id}", result); 
            });


               // すべての本を取得するエンドポイント
            app.MapGet("/books", async (IBookService bookService) =>
            {
                var result = await bookService.GetBooksAsync();
                return Results.Ok(result);
            });

            // IDで本を取得するエンドポイント
            app.MapGet("/books/{id:guid}", async (Guid id, IBookService bookService) =>
            {
                var result = await bookService.GetBookByIdAsync(id);
                return result != null ? Results.Ok(result) : Results.NotFound();
            });


            // IDで本を更新するエンドポイント
            app.MapPut("/books/{id:guid}", async (Guid id, UpdateBookRequest updateBookRequest, IBookService bookService) =>
            {
                var result = await bookService.UpdateBookAsync(id, updateBookRequest);
                return result != null ? Results.Ok(result) : Results.NotFound();
            });

            // IDで本を削除するエンドポイント
            app.MapDelete("/books/{id:guid}", async (Guid id, IBookService bookService) =>
            {
                var result = await bookService.DeleteBookAsync(id);
                return result ? Results.NoContent() : Results.NotFound();
            });

            return app;
        }
    }
}

おめでとうございます!本のAPIのためのすべてのエンドポイントを作成しました。これらのエンドポイントは、本のCRUD操作を処理し、リクエストとデータに基づいて適切なレスポンスを返します。

エンドポイントの登録方法

本のAPIのエンドポイントを定義した後、次のステップはこれらのエンドポイントをProgram.csファイルに登録することです。本のエンドポイントを登録するためにMapBookEndpointsメソッドを使用します。

また、Program.csクラスを整理して、組織化され、保守可能な状態を保つべきです。

// Program.cs

using System.Reflection;
using bookapi_minimal.Endpoints;
using bookapi_minimal.Services;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder(args);


builder.AddApplicationServices();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c=>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Mimal API", Version = "v1", Description = "Showing how you can build minimal " +
        "api with .net" });


    // Swagger JSONおよびUIのコメントパスを設定します。
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);

});
var app = builder.Build();

// HTTPリクエストパイプラインを構成します。
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseExceptionHandler();


app.MapGroup("/api/v1/")
   .WithTags(" Book endpoints")
   .MapBookEndPoint();

app.Run();

Program.csファイルの主要なコンポーネントを分解しましょう:

  • AddApplicationServices:このメソッドはAPIに必要なサービスを登録します。これは、依存関係注入コンテナーにサービスを追加するために以前に作成した拡張メソッドです。

  • AddSwaggerGen:このメソッドはSwaggerジェネレーターを登録し、APIのSwaggerドキュメントを作成するために使用されます。Swaggerドキュメント内でAPIのタイトル、バージョン、および説明を指定します。

  • MapGroup: このメソッドはエンドポイントをグループ化します。パスをパラメータとして受け取り、IEndpointRouteBuilderオブジェクトを返します。エンドポイントにタグを追加するためにWithTagsメソッドを使用し、書籍エンドポイントを登録するためにMapBookEndpointsメソッドを使用します。

  • Run: このメソッドはアプリケーションを起動します。

Swaggerドキュメントを有効にするには、GenerateDocumentationFileプロパティを.csprojファイルに追加する必要があります。この例では、ファイル名はbookapi-minimal.csprojですが、プロジェクトによって名前が異なる場合があります。

.csprojファイルに次の行を追加します:

<PropertyGroup>
  <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

最終的には、bookapi-minimal.csprojは以下のようになります:


<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <RootNamespace>bookapi_minimal</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
   <PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.2" />
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.6" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
  </ItemGroup>

</Project>

これでProgram.csファイルに書籍エンドポイントを登録したので、アプリケーションを実行し、Swaggerを使用してAPIエンドポイントをテストできます。

アプリケーションを実行すると、以下のURLでSwaggerドキュメントが表示されるはずです:https://localhost:5001/swagger/index.html。SwaggerドキュメントにはAPIエンドポイント、リクエストとレスポンスのモデルに関する情報が提供されており、ブラウザから直接エンドポイントをテストすることができます。次のような表示がされるはずです:

おめでとうございます!書籍サービスのビジネスロジックを実装し、カスタム例外を作成し、APIエンドポイントを定義し、Program.csファイルでエンドポイントを登録しました。また、Swaggerドキュメントを有効にしてAPIエンドポイントをテストできるようにしました。

データベースにシードデータを追加する方法

もう一つの重要なステップは、アプリケーション起動時にデータベースに初期データをシードすることです。このシードデータにより、データベースが初期化され、手動でデータを追加せずにAPIエンドポイントをテストできるようになります。

マイグレーションを実行し、APIエンドポイントをテストする前に、いくつかのシードデータを追加しましょう。

これを実現するために、Configurationフォルダに新しいクラスを作成し、BookTypeConfigurationsという名前を付け、以下のコードを追加します:



using bookapi_minimal.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace bookapi_minimal.Configurations
{
    public class BookTypeConfigurations : IEntityTypeConfiguration<BookModel>
    {
        public void Configure(EntityTypeBuilder<BookModel> builder)
        {
            // テーブル名の設定
            builder.ToTable("Books");

            // 主キーの設定
            builder.HasKey(x => x.Id);

            // プロパティの設定
            builder.Property(x => x.Id).ValueGeneratedOnAdd();
            builder.Property(x => x.Title).IsRequired().HasMaxLength(100);
            builder.Property(x => x.Author).IsRequired().HasMaxLength(100);
            builder.Property(x => x.Description).IsRequired().HasMaxLength(500);
            builder.Property(x => x.Category).IsRequired().HasMaxLength(100);
            builder.Property(x => x.Language).IsRequired().HasMaxLength(50);
            builder.Property(x => x.TotalPages).IsRequired();

            // シードデータ
            builder.HasData(
                new BookModel
                {
                    Id = Guid.NewGuid(),
                    Title = "The Alchemist",
                    Author = "Paulo Coelho",
                    Description = "The Alchemist follows the journey of an Andalusian shepherd",
                    Category = "Fiction",
                    Language = "English",
                    TotalPages = 208
                },
                new BookModel
                {
                    Id = Guid.NewGuid(),
                    Title = "To Kill a Mockingbird",
                    Author = "Harper Lee",
                    Description = "A novel about the serious issues of rape and racial inequality.",
                    Category = "Fiction",
                    Language = "English",
                    TotalPages = 281
                },
                new BookModel
                {
                    Id = Guid.NewGuid(),
                    Title = "1984",
                    Author = "George Orwell",
                    Description = "A dystopian social science fiction novel and cautionary tale about the dangers of totalitarianism. ",
                  Category = "Fiction",
                  Language = "English",
                  TotalPages = 328
                } 
            );
        }
    }
}

上記のコードを詳しく見ていきましょう:

Entity Framework Coreでは、IEntityTypeConfigurationインターフェースを使用してエンティティタイプとデータベースのシードデータを設定できます。BookTypeConfigurationsクラスはIEntityTypeConfiguration<BookModel>インターフェースを実装し、BookModelエンティティの設定を提供します。

  • Configureメソッド: このメソッドはBookModelエンティティタイプを設定するために使用されます。テーブル名、主キー、およびBookModelエンティティのプロパティを定義します。

    • テーブル名: ToTableメソッドはデータベースに作成されるテーブルの名前を指定します。この場合、テーブル名は「Books」と設定されています。

    • 主キー: HasKeyメソッドはBookModelエンティティの主キーを指定します。主キーはIdプロパティに設定されています。

    • プロパティ: PropertyメソッドはBookModelエンティティのプロパティを設定します。各プロパティのデータ型、長さ、および制約を指定します。

  • 初期データ: HasDataメソッドは、データベースに初期データを提供します。APIエンドポイントのテスト用に、3つのBookModelオブジェクトをサンプルデータで作成します。

これでBookTypeConfigurationsクラスを作成したので、この設定をApplicationContextクラスに登録する必要があります。これにより、データベースの作成または移行時にこの設定が適用されることが保証されます。

ようやくAPIのテストがほぼ準備できています。しかし、それをする前に、データベースを作成し、シードデータを適用するためのマイグレーションを実行する必要があります。

appsettings.jsonファイルにデータベース接続文字列を追加したことを覚えていますか?今、マイグレーションを実行し、後にデータベースを更新してマイグレーションが有効になるようにしましょう。

マイグレーションの実行方法

マイグレーションを使用すると、モデルクラスに加えられた変更に基づいてデータベーススキーマを更新できます。Entity Framework Coreでは、dotnet ef migrations addコマンドを使用して、これらの変更を反映した新しいマイグレーションを作成できます。

マイグレーションを実行するには、ターミナルで次のコマンドを実行します:

dotnet ef migrations add InitialCreate

コマンドが成功すると、次のような出力が表示されます:

Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'

プロジェクト内にMigrationsという新しいフォルダが表示されます。このフォルダには、モデルクラスに加えられた変更に基づいて作成されたマイグレーションファイルが含まれています。これらのマイグレーションファイルには、データベーススキーマを更新するためのSQLコマンドが含まれています。

データベースの更新方法

マイグレーションを作成した後、データベーススキーマを更新するためにマイグレーションを適用する必要があります。dotnet ef database updateコマンドを使用してマイグレーションを適用し、データベースを更新できます。SQL Serverが実行されていることを確認してください。

ターミナルで次のコマンドを実行します:


dotnet ef database update

これにより、モデルクラスに加えられた変更に基づいてデータベーススキーマが更新されます。データベース接続文字列にエラーがないことを確認してください。

APIエンドポイントのテスト方法

今すぐにSwaggerを使用してエンドポイントをテストすることができます。これを行うには、ターミナルで以下のコマンドを実行してアプリケーションを実行します:


dotnet run

これでアプリケーションが実行されます。ブラウザを開き、https://localhost:5001/swagger/index.htmlにアクセスしてSwaggerドキュメントを表示できます。APIエンドポイントの一覧、リクエストとレスポンスのモデル、およびブラウザから直接エンドポイントをテストする機能が表示されるはずです。

ポート番号が5001と異なる場合でも心配しないでください – それでも動作します。使用しているマシンの種類によってポートが変更されることがありますが、同じ結果が得られます。

Get All Booksエンドポイントのテスト方法

Get All Booksエンドポイントをテストするには、以下の手順に従ってください:

  1. Swaggerドキュメントで、GET /api/v1/booksエンドポイントをクリックします。

  2. Try it outボタンをクリックします。

  3. Executeボタンをクリックします。

これにより、データベース内のすべての本を取得するためのリクエストがAPIに送信されます。

APIからのレスポンスが表示され、データベースにシードされた本の一覧が含まれるはずです。

以下の画像はAPIからのレスポンスを示しています:

「IDで書籍を取得する」エンドポイントのテスト方法

「IDで書籍を取得する」エンドポイントをテストするには、以下の手順に従ってください:

  1. Swaggerドキュメントで、GET /api/v1/books/{id}エンドポイントをクリックします。

  2. idフィールドに書籍のIDを入力します。データベースにシードされた書籍IDのいずれかを使用できます。

  3. 試してみるボタンをクリックします。

これにより、指定されたIDの書籍を取得するためのリクエストがAPIに送信されます。APIからのレスポンスが表示され、指定されたIDの書籍が含まれるはずです。

以下の画像はAPIからのレスポンスを示しています:

「書籍を追加する」エンドポイントのテスト方法

「書籍を追加する」エンドポイントをテストするには、以下の手順に従ってください:

  1. Swaggerドキュメントで、POST /api/v1/booksエンドポイントをクリックします。

  2. 試してみるボタンをクリックします。

  3. リクエストボディに書籍の詳細を入力します。

  4. 実行」ボタンをクリックします。

これにより、新しい本をデータベースに追加するためのリクエストがAPIに送信されます。

APIからのレスポンスが表示され、新しく作成された本が含まれるはずです。

以下の画像は、APIからのレスポンスを示しています:

本を更新」エンドポイントのテスト方法

本を更新」エンドポイントをテストするには、以下の手順に従ってください:

  1. Swaggerドキュメントで、PUT /api/v1/books/{id}エンドポイントをクリックします。

  2. idフィールドに本のIDを入力します。今さっき追加した本のIDを使用できます。

  3. 試してみる」ボタンをクリックします。

これにより、指定されたIDの本を更新するためのリクエストがAPIに送信されます。

APIからのレスポンスが表示され、更新された本が含まれるはずです。

以下の画像は、APIからのレスポンスを示しています:

本を削除」エンドポイントのテスト方法

本を削除」エンドポイントをテストするには、以下の手順に従ってください:

  1. Swaggerドキュメントで、DELETE /api/v1/books/{id}エンドポイントをクリックします。

  2. idフィールドに本のIDを入力します。今追加した本のIDまたはシードデータのいずれかを使用できます。

  3. Try it outボタンをクリックします。

これにより、指定されたIDの本を削除するためのリクエストがAPIに送信されます。

以下の画像は、APIからのレスポンスを示しています:

おめでとうございます!本のCRUD操作をすべて実装し、Swaggerを使用してAPIエンドポイントをテストし、期待通りに動作することを確認しました。この基盤を活かして、APIにさらに機能を追加することができます。

結論

このハンドブックでは、ASP.NET Coreで.NET 8を使用して最小限のAPIを作成する方法を探りました。CRUD操作をサポートする包括的な本APIを構築し、カスタム例外を実装し、APIエンドポイントを定義および登録し、Swaggerドキュメントを有効にして簡単にテストできるようにしました。

このチュートリアルに従うことで、ASP.NET Coreで最小限のAPIを構築するための solid な基盤を得ました。この知識を活かして、さまざまな分野や業界のための堅牢なAPIを作成することができます。

このチュートリアルが役立つものであり、有益であったことを願っています。読んでいただきありがとうございます!

ソーシャルメディアで気軽にご連絡ください: