ASP.NET CoreでRESTful APIを構築する方法に関するすばらしいガイド

クリーンで保守可能なRESTful APIを実装する方法に関するステップバイステップガイド

ジェファーソン・サントスによる写真、Unsplashで公開

概要

RESTfulは新しい用語ではありません。 Webサービスがクライアントアプリとの間でデータを送受信するアーキテクチャスタイルを指します。これらのアプリケーションの目標は、さまざまなクライアントアプリが使用するデータを集中化することです。

RESTfulサービスを作成するための適切なツールを選択することは、スケーラビリティ、メンテナンス、ドキュメント、およびその他すべての関連する側面に注意する必要があるため、非常に重要です。 ASP.NET Coreは、これらの目標を達成するのに最適な強力で使いやすいAPIを提供します。

この記事では、ASP.NET Coreフレームワークを使用して、「ほぼ」現実世界のシナリオ向けに適切に構成されたRESTful APIを作成する方法を示します。開発プロセスを簡素化するために、一般的なパターンと戦略を詳しく説明します。

また、Entity Framework CoreやAutoMapperなどの一般的なフレームワークとライブラリを統合して、必要な機能を提供する方法も示します。

前提条件

オブジェクト指向プログラミングの概念の知識があることを期待しています。

C#プログラミング言語の多くの詳細を取り上げますが、このテーマの基本的な知識を持っていることをお勧めします。

また、RESTとは何か、HTTPプロトコルがどのように機能するか、APIエンドポイントとは何か、JSONとは何かを知っていると思います。このテーマに関する優れた入門チュートリアルです。最後の要件は、リレーショナルデータベースの機能を理解することです。

私と一緒にコーディングするには、.NET Core 2.2と、APIのテストに使用するツールであるPostmanをインストールする必要があります。 APIを開発するには、Visual Studio Codeなどのコードエディターを使用することをお勧めします。お好みのコードエディターを選択します。このコードエディターを選択する場合は、C#拡張機能をインストールして、コードの強調表示を改善することをお勧めします。

この記事の最後に、APIのGithubリポジトリへのリンクがあり、最終結果を確認できます。

スコープ

スーパーマーケット向けの架空のWeb APIを作成しましょう。次のスコープを実装する必要があると想像してみましょう。

  • クライアントアプリケーションがスーパーマーケットの製品カタログを管理できるようにするRESTfulサービスを作成します。乳製品や化粧品などの製品カテゴリを作成、読み取り、編集、削除するエンドポイントを公開し、これらのカテゴリの製品を管理する必要もあります。
  • カテゴリの場合、名前を保存する必要があります。製品については、名前、測定単位(重量で測定される製品のKGなど)、パッケージ内の数量(製品がビスケットのパックの場合は10など)、およびそれぞれのカテゴリを保存する必要があります。

この例を簡単にするために、在庫のある商品、商品の発送、セキュリティ、その他の機能は扱いません。指定されたスコープは、ASP.NET Coreの動作を示すのに十分です。

このサービスを開発するには、基本的に2つのAPIエンドポイントが必要です。1つはカテゴリを管理し、もう1つは製品を管理します。 JSON通信の観点から、応答は次のように考えることができます。

APIエンドポイント:/ api / categories

JSON応答(GET要求の場合):

{
  [
    {"id":1、 "name": "果物と野菜"}、
    {"id":2、 "name": "パン"}、
    …//その他のカテゴリ
  ]
}

APIエンドポイント:/ api / products

JSON応答(GET要求の場合):

{
  [
    {
      「id」:1、
      「名前」:「砂糖」、
      「quantityInPackage」:1
      「unitOfMeasurement」:「KG」
      "カテゴリー": {
        「id」:3
        「名前」:「砂糖」
      }
    }、
    …//その他の製品
  ]
}

アプリケーションの作成を始めましょう。

ステップ1 — APIを作成する

まず、Webサービスのフォルダー構造を作成し、次に.NET CLIツールを使用して基本的なWeb APIを作成する必要があります。端末またはコマンドプロンプトを開き(使用しているオペレーティングシステムによって異なります)、次のコマンドを順番に入力します。

mkdir src / Supermarket.API
cd src / Supermarket.API
dotnet new webapi

最初の2つのコマンドは、単にAPIの新しいディレクトリを作成し、現在の場所を新しいフォルダーに変更します。最後のものは、Web APIテンプレートに従って新しいプロジェクトを生成します。これは、開発中のアプリケーションの一種です。これらのコマンドおよび生成できる他のプロジェクトテンプレートの詳細については、このリンクを確認してください。

新しいディレクトリは次の構造になります。

プロジェクト構造

構造の概要

ASP.NET Coreアプリケーションは、Startupクラスで構成されたミドルウェアのグループ(アプリケーションパイプラインに接続されたアプリケーションの小さな部分で構成され、要求と応答を処理します)で構成されます。以前にExpress.jsなどのフレームワークを使用したことがある場合、この概念は新しいものではありません。

アプリケーションが起動すると、ProgramクラスからMainメソッドが呼び出されます。スタートアップコンフィギュレーションを使用してデフォルトのWebホストを作成し、特定のポート(デフォルトでは、HTTPの場合はポート5000、HTTPSの場合はポート5001)を介してHTTP経由でアプリケーションを公開します。

Controllersフォルダー内のValuesControllerクラスを見てください。 APIがルート/ api / valuesを介してリクエストを受信したときに呼び出されるメソッドを公開します。

このコードの一部を理解していなくても心配しないでください。必要なAPIエンドポイントを開発するときに、それぞれについて詳しく説明します。とりあえず、このクラスを削除します。使用しないからです。

ステップ2 —ドメインモデルの作成

アプリケーションをシンプルかつ維持しやすくするために、いくつかの設計コンセプトを適用します。

自分で理解して保守できるコードを書くことはそれほど難しくありませんが、チームの一員として働くことに留意する必要があります。コードの書き方に注意を払わないと、結果はモンスターになり、チームメイトに常に頭痛の種を与えます。極端に聞こえますよね?しかし、信じてください、それが真実です。

wtf — smitty42によるコード品質測定はCC-BY-ND 2.0でライセンスされています

ドメイン層を書くことから始めましょう。この層には、モデルクラス、製品とカテゴリを表すクラス、およびリポジトリとサービスインターフェイスがあります。これらの最後の2つの概念については、後ほど説明します。

Supermarket.APIディレクトリ内に、Domainという名前の新しいフォルダーを作成します。新しいドメインフォルダー内に、Modelsという別のフォルダーを作成します。このフォルダに追加する必要がある最初のモデルはカテゴリです。最初は、単純なPlain Old CLR Object(POCO)クラスになります。つまり、クラスには基本情報を記述するためのプロパティのみがあります。

このクラスには、カテゴリを識別するためのIdプロパティとNamepropertyがあります。 Productsプロパティもあります。この最後のものは、ほとんどのASP.NET Coreアプリケーションがデータをデータベースに保存し、カテゴリと製品間の関係をマッピングするために使用するORMであるEntity Framework Coreによって使用されます。カテゴリには多くの関連製品があるため、オブジェクト指向プログラミングの観点から考えるのも理にかなっています。

製品モデルも作成する必要があります。同じフォルダーで、新しいProductクラスを追加します。

製品には、IDと名前のプロパティもあります。は、1つのパックに含まれる製品のユニット数(アプリケーションスコープのビスケットの例を思い出してください)とUnitOfMeasurementプロパティを示す、QuantityInPackageプロパティでもあります。これは列挙型で表され、可能な測定単位の列挙を表します。最後の2つのプロパティ、CategoryIdとCategoryは、製品とカテゴリ間の関係をマップするためにORMによって使用されます。製品にはカテゴリが1つだけあることを示します。

ドメインモデルの最後の部分であるEUnitOfMeasurement列挙型を定義しましょう。

慣例により、enumは名前の前に「E」で始まる必要はありませんが、一部のライブラリおよびフレームワークでは、enumをインターフェースおよびクラスと区別する方法としてこのプレフィックスがあります。

コードは本当に簡単です。ここでは、測定単位についていくつかの可能性のみを定義しましたが、実際のスーパーマーケットシステムでは、他の多くの測定単位があり、そのための個別のモデルがある場合があります。

すべての列挙の可能性に適用されるDescription属性に注意してください。属性は、クラス、インターフェイス、プロパティ、およびC#言語の他のコンポーネントのメタデータを定義する方法です。この場合、製品APIエンドポイントの応答を簡素化するために使用しますが、今のところ気にする必要はありません。後でここに戻ってきます。

基本モデルはすぐに使用できます。これで、すべてのカテゴリを管理するAPIエンドポイントの作成を開始できます。

ステップ3 —カテゴリーAPI

Controllersフォルダーに、CategoriesControllerという新しいクラスを追加します。

慣例により、接尾辞「Controller」で終わるこのフォルダー内のすべてのクラスは、アプリケーションのコントローラーになります。それは彼らがリクエストとレスポンスを処理することを意味します。名前空間Microsoft.AspNetCore.Mvcで定義されているControllerクラスからこのクラスを継承する必要があります。

名前空間は、関連するクラス、インターフェイス、列挙、および構造体のグループで構成されます。これは、Javascript言語のモジュール、またはJavaのパッケージに似たものと考えることができます。

新しいコントローラーは、/ api / categoriesルートを介して応答する必要があります。これを実現するには、クラス名の上にRoute属性を追加し、慣例により、ルートがコントローラーサフィックスなしでクラス名を使用することを示すプレースホルダーを指定します。

GETリクエストの処理を始めましょう。まず、誰かがGET動詞を介して/ api / categoriesからデータを要求すると、APIはすべてのカテゴリーを返す必要があります。この目的のためにカテゴリサービスを作成できます。

概念的には、サービスは基本的に、ビジネスロジックを処理するメソッドを定義するクラスまたはインターフェイスです。多くの異なるプログラミング言語では、認証と承認、支払い、複雑なデータフロー、キャッシング、他のサービスやモデル間の相互作用を必要とするタスクなどのビジネスロジックを処理するサービスを作成することが一般的です。

サービスを使用して、タスクを完了するために必要な実際のロジックから要求と応答の処理を分離できます。

最初に作成するサービスは、単一の動作またはメソッド、つまりリストメソッドを定義します。このメソッドは、データベース内のすべての既存のカテゴリを返すと予想されます。

簡単にするために、この場合はデータのページ分割やフィルタリングを扱いません。これらの機能を簡単に処理する方法を示す記事を今後作成します。

C#(および、たとえばJavaなどの他のオブジェクト指向言語)で何かの予想される動作を定義するには、インターフェイスを定義します。インターフェイスは、何かがどのように機能するかを指示しますが、動作の実際のロジックは実装しません。ロジックは、インターフェイスを実装するクラスに実装されます。この概念が明確でない場合でも心配しないでください。しばらくすると理解できます。

Domainフォルダ内に、Servicesという名前の新しいディレクトリを作成します。そこで、ICategoryServiceというインターフェイスを追加します。慣例により、すべてのインターフェイスはC#で大文字の「I」で始まる必要があります。インターフェイスコードを次のように定義します。

ListAsyncメソッドの実装は、カテゴリの列挙を非同期的に返す必要があります。

戻り値をカプセル化するTaskクラスは、非同期を示します。データベースが何らかの操作を完了してデータを返すまで待機する必要があるため、このプロセスには時間がかかる可能性があるため、非同期メソッドで考える必要があります。 「async」サフィックスにも注意してください。これは、メソッドを非同期で実行する必要があることを示す規則です。

たくさんの慣習がありますよね?個人的に気に入っています。なぜなら、.NETテクノロジーを使用している会社に慣れていない場合でも、アプリケーションが読みやすくなるからです。

「-OK、このインターフェイスを定義しましたが、何もしません。どのように役立つのでしょうか?」

Javascriptや別の強く型付けされていない言語などの言語を使用している場合、この概念は奇妙に思えるかもしれません。

インターフェイスを使用すると、実際の実装から目的の動作を抽象化できます。依存性注入と呼ばれるメカニズムを使用して、これらのインターフェイスを実装し、他のコンポーネントから分離できます。

基本的に、依存性注入を使用するときは、インターフェイスを使用していくつかの動作を定義します。次に、インターフェイスを実装するクラスを作成します。最後に、インターフェイスからの参照を作成したクラスにバインドします。

「-本当に紛らわしいですね。これらのことを行うクラスを単純に作成することはできませんか?」

APIの実装を続けましょう。このアプローチを使用する理由を理解できます。

CategoriesControllerコードを次のように変更します。

コントローラーのコンストラクター関数を定義し(クラスの新しいインスタンスが作成されると、コンストラクターが呼び出されます)、ICategoryServiceのインスタンスを受け取ります。インスタンスは、サービスインターフェイスを実装するものであれば何でもかまいません。このインスタンスは、読み取り専用のプライベートフィールド_categoryServiceに保存します。このフィールドを使用して、カテゴリサービスの実装のメソッドにアクセスします。

ところで、アンダースコア接頭辞は、フィールドを示すもう1つの一般的な規則です。この規則は、特に.NETの公式の命名規則ガイドラインでは推奨されていませんが、「this」キーワードを使用してローカル変数とクラスフィールドを区別する必要がないようにするための非常に一般的な方法です。個人的には読みやすいと思いますし、多くのフレームワークとライブラリがこの規則を使用しています。

コンストラクターの下で、/ api / categoriesのリクエストを処理するメソッドを定義しました。 HttpGet属性は、ASP.NET Coreパイプラインにそれを使用してGETリクエストを処理するように指示します(この属性は省略できますが、読みやすくするために記述する方が良いです)。

このメソッドは、カテゴリサービスインスタンスを使用してすべてのカテゴリをリストし、そのカテゴリをクライアントに返します。フレームワークパイプラインは、JSONオブジェクトへのデータのシリアル化を処理します。 IEnumerable typeは、カテゴリの列挙を返すことをフレームワークに指示し、asyncキーワードに続くTaskタイプは、このメソッドを非同期に実行する必要があることをパイプラインに指示します。最後に、非同期メソッドを定義するとき、時間がかかるタスクにはawaitキーワードを使用する必要があります。

OK、APIの初期構造を定義しました。ここで、実際にカテゴリサービスを実装する必要があります。

手順4 —カテゴリサービスの実装

APIのルートフォルダー(Supermarket.APIフォルダー)で、Servicesという名前の新しいフォルダーを作成します。ここに、すべてのサービスの実装を配置します。新しいフォルダー内に、CategoryServiceという新しいクラスを追加します。コードを次のように変更します。

これはインターフェース実装の基本的なコードにすぎませんが、まだロジックを処理していません。リスト方法の仕組みを考えてみましょう。

データベースにアクセスし、すべてのカテゴリを返す必要があります。その後、このデータをクライアントに返す必要があります。

サービスクラスは、データアクセスを処理するクラスではありません。データベースからのデータを管理するために使用されるリポジトリパターンと呼ばれるパターンがあります。

リポジトリパターンを使用する場合、基本的にすべてのロジックをカプセル化してデータアクセスを処理するリポジトリクラスを定義します。これらのリポジトリは、コレクションを操作するのと同じ方法で、特定のモデルのオブジェクトをリスト、作成、編集、削除するメソッドを公開します。内部的に、これらのメソッドはデータベースと通信してCRUD操作を実行し、データベースアクセスを他のアプリケーションから分離します。

サービスは、オブジェクトのリストを取得するために、カテゴリリポジトリと通信する必要があります。

概念的には、サービスは1つ以上のリポジトリまたは他のサービスと「対話」して操作を実行できます。

データアクセスロジックを処理するための新しい定義を作成するのは冗長に思えるかもしれませんが、しばらくすると、このロジックをサービスクラスから分離することが本当に有利であることがわかります。

カテゴリを保持する方法として、データベース通信の仲介を担当するリポジトリを作成しましょう。

手順5 —カテゴリリポジトリと永続層

Domainフォルダ内に、Repositoriesという新しいディレクトリを作成します。次に、ICategoryRespositoryという新しいインターフェイスを追加します。次のようにインターフェースを定義します。

初期コードは基本的にサービスインターフェイスのコードと同じです。

インターフェイスを定義したら、サービスクラスに戻り、ICategoryRepositoryのインスタンスを使用してデータを返すリスティングメソッドの実装を完了できます。

ここで、カテゴリリポジトリの実際のロジックを実装する必要があります。それを行う前に、データベースにどのようにアクセスするかを考える必要があります。

ところで、まだデータベースはありません!

データベースORMとして、Entity Framework Core(簡単にするためにEF Coreと呼びます)を使用します。このフレームワークにはASP.NET CoreがデフォルトのORMとして付属しており、アプリケーションのクラスをデータベーステーブルにマップできるようにするフレンドリーなAPIを公開しています。

EF Coreでは、最初にアプリケーションを設計してから、コードで定義した内容に従ってデータベースを生成することもできます。この手法は、最初のコードと呼ばれます。コードファーストアプローチを使用してデータベースを生成します(この例では、実際にはインメモリデータベースを使用しますが、SQL ServerまたはMySQLサーバーインスタンスに簡単に変更できますが、例えば)。

APIのルートフォルダーに、Persistenceという名前の新しいディレクトリを作成します。このディレクトリには、リポジトリの実装など、データベースにアクセスするために必要なすべてのものが含まれます。

新しいフォルダー内に、Contextsという名前の新しいディレクトリを作成し、AppDbContextという名前の新しいクラスを追加します。このクラスは、EF Coreがモデルをデータベーステーブルにマップするために使用するDbContextを継承する必要があります。次の方法でコードを変更します。

このクラスに追加したコンストラクターは、依存関係注入を通じてデータベース構成を基本クラスに渡す役割を果たします。これがどのように機能するかすぐにわかります

次に、2つのDbSetプロパティを作成する必要があります。これらのプロパティは、モデルをデータベーステーブルにマップするセット(一意のオブジェクトのコレクション)です。

また、モデルのプロパティをそれぞれのテーブル列にマッピングし、どのプロパティが主キーであるか、外部キーであるか、列タイプなどを指定する必要があります。これを行うには、Fluent APIデータベースマッピングを指定します。 AppDbContextクラスを次のように変更します。

コードは直感的です。

モデルをマッピングするテーブルを指定します。また、メソッドHasKey、テーブル列、Propertyメソッド、およびIsRequired、HasMaxLength、ValueGeneratedOnAddなどのいくつかの制約を使用して、主キーを設定します。

次のコードをご覧ください。

builder.Entity ()
       .HasMany(p => p.Products)
       .WithOne(p => p.Category)
       .HasForeignKey(p => p.CategoryId);

ここでは、テーブル間の関係を指定しています。カテゴリには多くの製品があると言い、この関係をマッピングするプロパティを設定します(カテゴリクラスの製品、および製品クラスのカテゴリ)。また、外部キー(CategoryId)も設定します。

EF Coreを使用して1対1および多対多の関係を構成する方法、および全体として使用する方法を学習する場合は、このチュートリアルをご覧ください。

HasDataメソッドを使用して、データをシードするための構成もあります。

builder.Entity ()。HasData
  新しいカテゴリ{Id = 100、名前= "Fruits and Vegetables"}、
  新しいカテゴリ{ID = 101、名前= "Dairy"}
);

ここでは、デフォルトで2つのサンプルカテゴリを追加します。終了後にAPIエンドポイントをテストするために必要です。

注意:インメモリプロバイダーが動作するために必要なため、ここでIdプロパティを手動で設定しています。自動生成された識別子とシードデータの衝突を避けるために、識別子を大きな数字に設定しています。
この制限は真のリレーショナルデータベースプロバイダーには存在しないため、たとえばSQL Serverなどのデータベースを使用する場合は、これらの識別子を指定する必要はありません。この動作を理解する場合は、このGithubの問題を確認してください。

データベースコンテキストクラスを実装したら、カテゴリリポジトリを実装できます。 Persistenceフォルダー内にRepositoriesという新しいフォルダーを追加してから、BaseRepositoryという新しいクラスを追加します。

このクラスは、すべてのリポジトリが継承する抽象クラスです。抽象クラスは、直接インスタンスを持たないクラスです。インスタンスを作成するには、直接クラスを作成する必要があります。

BaseRepositoryは、依存性注入を通じてAppDbContextのインスタンスを受け取り、データベース操作を処理するために必要なすべてのメソッドへのアクセスを提供する_contextと呼ばれる保護プロパティ(子クラスのみがアクセスできるプロパティ)を公開します。

CategoryRepositoryという同じフォルダーに新しいクラスを追加します。次に、リポジトリロジックを実際に実装します。

リポジトリはBaseRepositoryを継承し、ICategoryRepositoryを実装します。

リストメソッドを実装するのがいかに簡単であるかに注目してください。 Categoriesデータベースセットを使用してカテゴリテーブルにアクセスし、拡張メソッドToListAsyncを呼び出します。このメソッドは、クエリの結果をカテゴリのコレクションに変換します。

EF Coreは、メソッド呼び出しをSQLクエリに変換します。これは可能な限り最も効率的な方法です。クエリは、データをコレクションに変換するメソッドを呼び出したとき、またはメソッドを使用して特定のデータを取得したときにのみ実行されます。

カテゴリコントローラー、サービス、およびリポジトリのクリーンな実装ができました。

懸念事項を分離し、想定されていることだけを行うクラスを作成しました。

アプリケーションをテストする前の最後の手順は、ASP.NET Core依存性注入メカニズムを使用して、インターフェイスをそれぞれのクラスにバインドすることです。

ステップ6 —依存性注入の構成

この概念がどのように機能するかを最終的に理解する時が来ました。

アプリケーションのルートフォルダーで、スタートアップクラスを開きます。このクラスは、アプリケーションの起動時にあらゆる種類の構成を構成します。

ConfigureServicesおよびConfigureメソッドは、フレームワークパイプラインによって実行時に呼び出され、アプリケーションの動作方法と使用するコンポーネントを設定します。

ConfigureServicesメソッドをご覧ください。ここには、MVCパイプラインを使用するようにアプリケーションを構成する1行しかありません。これは、基本的に、アプリケーションがコントローラークラスを使用して要求と応答を処理することを意味します今のところ)。

ConfigureServicesメソッドを使用して、servicesパラメーターにアクセスし、依存関係のバインドを構成できます。クラスコードをクリーンアップして、すべてのコメントを削除し、コードを次のように変更します。

次のコードを見てください:

services.AddDbContext (options => {
  options.UseInMemoryDatabase( "supermarket-api-in-memory");
});

ここで、データベースコンテキストを構成します。メソッドに引数として渡された文字列によって識別されるメモリ内データベース実装でAppDbContextを使用するようにASP.NET Coreに指示します。通常、インメモリプロバイダーは統合テストを作成するときに使用されますが、ここでは簡単にするために使用しています。この方法では、アプリケーションをテストするために実際のデータベースに接続する必要はありません。

これらの行の構成は、スコープライフタイムを使用した依存関係注入のためにデータベースコンテキストを内部的に構成します。

スコープライフタイムは、コンストラクター引数としてAppDbContextのインスタンスを受け取るクラスを解決する必要があるたびに、クラスの同じインスタンスを使用する必要があることをASP.NET Coreパイプラインに伝えます。メモリにインスタンスがない場合、パイプラインは新しいインスタンスを作成し、指定されたリクエスト中にそれを必要とするすべてのクラスで再利用します。これにより、使用する必要があるときにクラスインスタンスを手動で作成する必要がなくなります。

公式ドキュメントを読んで確認できる他のライフタイムスコープがあります。

依存性注入手法には、次のような多くの利点があります。

  • コードの再利用性。
  • 実装を変更する必要があるときに、その機能を使用する100か所を変更する必要がないため、生産性が向上します。
  • インターフェイスをコンストラクター引数として渡す必要があるモック(クラスの偽の実装)を使用してテストする必要があるものを分離できるため、アプリケーションを簡単にテストできます。
  • クラスがコンストラクターを介してより多くの依存関係を受け取る必要がある場合、インスタンスが作成されているすべての場所を手動で変更する必要はありません(それは素晴らしいです!)。

データベースコンテキストを構成した後、サービスとリポジトリをそれぞれのクラスにバインドします。

services.AddScoped ();
services.AddScoped ();

これらのクラスは内部的にデータベースコンテキストクラスを使用する必要があるため、ここではスコープライフタイムも使用します。この場合、同じスコープを指定することは理にかなっています。

依存関係バインディングを構成したら、データベースが初期データを正しくシードするために、Programクラスで小さな変更を行う必要があります。この手順は、インメモリデータベースプロバイダーを使用する場合にのみ必要です(理由を理解するには、このGithubの問題を参照してください)。

インメモリプロバイダーを使用しているため、アプリケーションの起動時にデータベースが「作成」されるように、Mainメソッドを変更する必要がありました。この変更がないと、シードするカテゴリは作成されません。

すべての基本機能を実装したら、APIエンドポイントをテストします。

ステップ7 —カテゴリーAPIのテスト

APIルートフォルダーでターミナルまたはコマンドプロンプトを開き、次のコマンドを入力します。

ドットネットラン

上記のコマンドは、アプリケーションを起動します。コンソールには、次のような出力が表示されます。

info:Microsoft.EntityFrameworkCore.Infrastructure [10403]
Entity Framework Core 2.2.0-rtm-35687は、オプション「StoreName = supermarket-api-in-memory」でプロバイダー「Microsoft.EntityFrameworkCore.InMemory」を使用して「AppDbContext」を初期化しました
info:Microsoft.EntityFrameworkCore.Update [30100]
インメモリストアに2つのエンティティを保存しました。
info:Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager [0]
ユーザープロファイルが利用可能です。 「C:\ Users \ evgomes \ AppData \ Local \ ASP.NET \ DataProtection-Keys」をキーリポジトリとして使用し、Windows DPAPIを使用して保存中のキーを暗号化します。
ホスティング環境:開発
コンテンツルートパス:C:\ Users \ evgomes \ Desktop \ Tutorials \ src \ Supermarket.API
リスニング中:https:// localhost:5001
リスニング中:http:// localhost:5000
アプリケーションが開始されました。 Ctrl + Cを押してシャットダウンします。

データベースを初期化するためにEF Coreが呼び出されたことがわかります。最後の行は、アプリケーションが実行されているポートを示しています。

ブラウザーを開き、http:// localhost:5000 / api / categories(またはコンソール出力に表示されるURL)に移動します。 HTTPSが原因でセキュリティエラーが表示される場合は、アプリケーションの例外を追加してください。

ブラウザは、出力として次のJSONデータを表示します。

[
  {
     「id」:100、
     「名前」:「果物と野菜」、
     「製品」:[]
  }、
  {
     「id」:101、
     「name」:「Dairy」、
     「製品」:[]
  }
]

ここに、データベースコンテキストを構成したときにデータベースに追加したデータが表示されます。この出力は、コードが機能していることを確認します。

本当に数行のコードでGET APIエンドポイントを作成し、APIのアーキテクチャにより変更が非常に簡単なコード構造を持っています。

次に、ビジネスニーズに応じて調整する必要がある場合に、このコードを簡単に変更できることを示します。

手順8 —カテゴリリソースの作成

APIエンドポイントの仕様を覚えていれば、実際のJSONレスポンスには1つの追加のプロパティ、つまり製品の配列があることに気づきました。希望する応答の例を見てください。

{
  [
    {"id":1、 "name": "果物と野菜"}、
    {"id":2、 "name": "パン"}、
    …//その他のカテゴリ
  ]
}

カテゴリモデルには、指定されたカテゴリの製品をマップするためにEF Coreが必要とするProductsプロパティがあるため、現在のJSON応答にproducts配列が存在します。

応答にこのプロパティは必要ありませんが、モデルクラスを変更してこのプロパティを除外することはできません。カテゴリデータを管理しようとするとEF Coreがエラーをスローします。また、製品を持たない製品カテゴリを持つことは意味がないため、ドメインモデルの設計を破壊します。

スーパーマーケットカテゴリの識別子と名前のみを含むJSONデータを返すには、リソースクラスを作成する必要があります。

リソースクラスは、特定の情報を表すために、一般的にJSONデータの形式でクライアントアプリケーションとAPIエンドポイント間で交換される基本情報のみを含むクラスです。

APIエンドポイントからのすべての応答は、リソースを返す必要があります。

応答として実際のモデル表現を返すことは悪い習慣です。これには、クライアントアプリケーションが必要としない情報や持つ許可がない情報を含めることができるためです(たとえば、ユーザーモデルはユーザーパスワードの情報を返すことができます) 、これは大きなセキュリティ問題になります)。

製品なしで、カテゴリのみを表すリソースが必要です。

リソースが何であるかがわかったので、それを実装しましょう。まず、コマンドラインでCtrl + Cを押して、実行中のアプリケーションを停止します。アプリケーションのルートフォルダーに、Resourcesという名前の新しいフォルダーを作成します。そこで、CategoryResourceという新しいクラスを追加します。

カテゴリサービスによって提供されるカテゴリモデルのコレクションをカテゴリリソースのコレクションにマップする必要があります。

AutoMapperというライブラリを使用して、オブジェクト間のマッピングを処理します。 AutoMapperは、.NETの世界で非常に人気のあるライブラリであり、多くの商用およびオープンソースプロジェクトで使用されています。

AutoMapperをアプリケーションに追加するには、コマンドラインに次の行を入力します。

dotnet addパッケージAutoMapper
dotnet addパッケージAutoMapper.Extensions.Microsoft.DependencyInjection

AutoMapperを使用するには、2つのことを行う必要があります。

  • 依存性注入のために登録します。
  • AutoMapperにクラスマッピングの処理方法を指示するクラスを作成します。

まず、Startupクラスを開きます。 ConfigureServicesメソッドの最後の行の後に、次のコードを追加します。

services.AddAutoMapper();

この行は、依存関係の注入のための登録や、起動中のアプリケーションのスキャンによるマッピングプロファイルの構成など、AutoMapperの必要なすべての構成を処理します。

次に、ルートディレクトリにMappingという新しいフォルダーを追加し、ModelToResourceProfileというクラスを追加します。この方法でコードを変更します。

このクラスは、AutoMapperがマッピングの動作を確認するために使用するクラスタイプであるプロファイルを継承します。コンストラクターで、CategoryモデルクラスとCategoryResourceクラスの間にマップを作成します。クラスのプロパティは同じ名前とタイプを持っているため、特別な構成を使用する必要はありません。

最後の手順では、AutoMapperを使用してオブジェクトマッピングを処理するように、カテゴリコントローラーを変更します。

IMapper実装のインスタンスを受け取るようにコンストラクターを変更しました。これらのインターフェイスメソッドを使用して、AutoMapperマッピングメソッドを使用できます。

また、GetAllAsyncメソッドを変更して、Mapメソッドを使用してカテゴリの列挙をリソースの列挙にマップしました。このメソッドは、マップするクラスまたはコレクションのインスタンスを受け取り、一般的な型定義を介して、どのタイプのクラスまたはコレクションにマップする必要があるかを定義します。

コンストラクターに新しい依存関係(IMapper)を注入するだけで、サービスクラスやリポジトリを適合させることなく、実装を簡単に変更できることに注意してください。

依存性注入により、機能を追加または削除するためにすべてのコード実装を壊す必要がないため、アプリケーションを保守しやすく変更しやすくなります。

おそらく、コントローラークラスだけでなく、依存関係を受け取るすべてのクラス(依存関係自体を含む)が自動的に解決され、バインディング構成に従って正しいクラスを受け取ることに気付いたでしょう。

依存性注入は驚くべきことではありませんか?

次に、dotnet runコマンドを使用してAPIを再度起動し、http:// localhost:5000 / api / categoriesに移動して、新しいJSON応答を確認します。

これは、表示されるはずの応答データです

既にGETエンドポイントがあります。それでは、カテゴリをPOST(作成)するための新しいエンドポイントを作成しましょう。

ステップ9 —新しいカテゴリを作成する

リソースの作成を扱う場合、次のような多くのことに注意する必要があります。

  • データの検証とデータの整合性。
  • リソースを作成する権限。
  • エラー処理;
  • ロギング。

このチュートリアルでは認証と承認の処理方法を説明しませんが、JSON Webトークン認証に関するチュートリアルを読むと、これらの機能を簡単に実装する方法がわかります。

また、アプリケーションで使用できるセキュリティおよびユーザー登録に関する組み込みソリューションを提供するASP.NET Identityと呼ばれる非常に人気のあるフレームワークがあります。使用可能な組み込みのIdentityDbContextなど、EF Coreと連携するプロバイダーが含まれています。詳細については、こちらをご覧ください。

他のシナリオをカバーするHTTP POSTエンドポイントを作成しましょう(ロギングを除き、スコープとツールによって異なる場合があります)。

新しいエンドポイントを作成する前に、新しいリソースが必要です。このリソースは、クライアントアプリケーションがこのエンドポイントに送信するデータ(この場合はカテゴリ名)をアプリケーションのクラスにマップします。

新しいカテゴリを作成しているため、まだIDがありません。つまり、名前のみを含むカテゴリを表すリソースが必要です。

Resourcesフォルダーに、SaveCategoryResourceという新しいクラスを追加します。

NameプロパティにRequired属性とMaxLength属性が適用されていることに注意してください。これらの属性はデータ注釈と呼ばれます。 ASP.NET Coreパイプラインは、このメタデータを使用して要求と応答を検証します。名前が示すように、カテゴリ名は必須であり、最大長は30文字です。

次に、新しいAPIエンドポイントの形状を定義します。次のコードをカテゴリコントローラーに追加します。

フレームワークに、これがHttpPost属性を使用してHTTP POSTエンドポイントであることを伝えます。

このメソッドの応答タイプであるTask に注目してください。コントローラクラスに存在するメソッドはアクションと呼ばれ、アプリケーションがアクションを実行した後に複数の結果を返すことができるため、このシグネチャがあります。

この場合、カテゴリ名が無効である場合、または何か問題が発生した場合、通常、クライアントアプリが問題の処理に使用できるエラーメッセージを含む400コード(不正な要求)応答を返す必要があります。すべてが正常に実行された場合、データを含む200応答(成功)。

応答として使用できるアクションの種類には多くの種類がありますが、通常、このインターフェイスを使用でき、ASP.NET Coreはそのためにデフォルトクラスを使用します。

FromBody属性は、ASP.NET Coreにリクエスト本文データを新しいリソースクラスに解析するように指示します。つまり、カテゴリ名を含むJSONがアプリケーションに送信されると、フレームワークはそれを自動的に新しいクラスに解析します。

それでは、ルートロジックを実装しましょう。新しいカテゴリを作成するには、いくつかの手順を実行する必要があります。

  • まず、着信リクエストを検証する必要があります。リクエストが無効な場合、エラーメッセージを含む不正なリクエストレスポンスを返す必要があります。
  • 次に、リクエストが有効である場合、AutoMapperを使用して新しいリソースをカテゴリモデルクラスにマップする必要があります。
  • 次に、新しいカテゴリを保存するようにサービスに通知する必要があります。保存ロジックが問題なく実行されると、新しいカテゴリデータを含む応答が返されます。そうでない場合は、プロセスが失敗したことを示す情報と、潜在的なエラーメッセージが表示されます。
  • 最後に、エラーがある場合、不正なリクエストを返します。そうでない場合は、新しいカテゴリモデルをカテゴリリソースにマップし、新しいカテゴリデータを含む成功応答をクライアントに返します。

複雑に思えますが、API用に構造化したサービスアーキテクチャを使用してこのロジックを実装するのは本当に簡単です。

着信リクエストを検証することから始めましょう。

ステップ10 —モデル状態を使用してリクエスト本文を検証する

ASP.NET Coreコントローラーには、ModelStateというプロパティがあります。このプロパティは、リクエストの実行中にアクションの実行に到達する前に入力されます。これは、ModelStateDictionaryのインスタンスです。ModelStateDictionaryは、リクエストが有効かどうかや検証エラーメッセージの可能性などの情報を含むクラスです。

エンドポイントコードを次のように変更します。

コードは、モデルの状態(この場合、リクエスト本文で送信されたデータ)が無効かどうかをチェックし、データアノテーションをチェックします。そうでない場合、APIは不正なリクエスト(ステータスコード400)を返し、デフォルトのエラーメッセージがアノテーションメタデータを提供します。

ModelState.GetErrorMessages()メソッドはまだ実装されていません。これは、検証メソッドを単純な文字列に変換してクライアントに返すために実装する拡張メソッド(既存のクラスまたはインターフェイスの機能を拡張するメソッド)です。

APIのルートに新しいフォルダーExtensionsを追加してから、新しいクラスModelStateExtensionsを追加します。

すべての拡張メソッドは、それらが宣言されているクラスと同様に静的である必要があります。つまり、特定のインスタンスデータを処理せず、アプリケーションの起動時に1回だけ読み込まれます。

パラメータ宣言の前にあるthisキーワードは、C#コンパイラに拡張メソッドとして扱うように指示します。その結果、拡張機能を使用する場所にそれぞれのusingディレクティブを含めるため、このクラスの通常のメソッドのように呼び出すことができます。

この拡張機能はLINQクエリを使用します。LINQクエリは.NETの非常に便利な機能で、チェーン可能な式を使用してデータをクエリおよび変換できます。ここの式は、検証エラーメソッドをエラーメッセージを含む文字列のリストに変換します。

次の手順に進む前に、名前空間Supermarket.API.Extensionsをカテゴリコントローラーにインポートします。

Supermarket.API.Extensionsを使用。

新しいリソースをカテゴリモデルクラスにマッピングして、エンドポイントロジックの実装を続けましょう。

手順11 —新しいリソースのマッピング

モデルをリソースに変換するマッピングプロファイルを既に定義しています。ここで、逆を行う新しいプロファイルが必要です。

新しいクラスResourceToModelProfileをMappingフォルダーに追加します。

ここに新しいものはありません。依存性注入の魔法のおかげで、AutoMapperはアプリケーションの起動時にこのプロファイルを自動的に登録します。使用するために他の場所を変更する必要はありません。

これで、新しいリソースをそれぞれのモデルクラスにマップできます。

手順12 —要求/応答パターンを適用して保存ロジックを処理する

次に、最も興味深いロジックを実装する必要があります。新しいカテゴリを保存するためです。私たちのサービスはそれを期待しています。

保存ロジックは、データベースへの接続時の問題、または内部ビジネスルールがデータを無効にするために失敗することがあります。

何か問題が発生した場合、APIを停止する可能性があり、クライアントアプリケーションは問題の処理方法を認識できないため、単純にエラーをスローすることはできません。また、エラーをログに記録するロギングメカニズムが潜在的にあります。

保存メソッドのコントラクト、つまり、メソッドの署名と応答タイプは、プロセスが正しく実行されたかどうかを示す必要があります。プロセスに問題がなければ、カテゴリデータを受け取ります。そうでない場合は、少なくとも、プロセスが失敗した理由を示すエラーメッセージを受信する必要があります。

要求/応答パターンを適用することにより、この機能を実装できます。このエンタープライズ設計パターンは、サービスが何らかのタスクを処理し、サービスを使用しているクラスに情報を返すために使用する情報をカプセル化する方法として、リクエストとレスポンスのパラメーターをクラスにカプセル化します。

このパターンには、次のようないくつかの利点があります。

  • より多くのパラメーターを受け取るためにサービスを変更する必要がある場合、その署名を破る必要はありません。
  • リクエストやレスポンスの標準的な契約を定義できます。
  • アプリケーションプロセスを停止せずにビジネスロジックと潜在的な障害を処理できます。また、大量のtry-catchブロックを使用する必要はありません。

データの変更を処理するサービスメソッドの標準応答タイプを作成しましょう。このタイプのすべてのリクエストについて、リクエストが問題なく実行されたかどうかを知りたいです。失敗した場合は、クライアントにエラーメッセージを返します。

[サービス]内の[ドメイン]フォルダーに、Communicationという新しいディレクトリを追加します。 BaseResponseという新しいクラスをそこに追加します。

これは、応答タイプが継承する抽象クラスです。

抽象化は、要求が正常に完了したかどうかを示すSuccessプロパティと、何かが失敗した場合にエラーメッセージを表示するMessageプロパティを定義します。

これらのプロパティは必須であり、子クラスはコンストラクタ関数を介してこの情報を渡す必要があるため、継承されたクラスのみがこのデータを設定できることに注意してください。

ヒント:すべての基本クラスを定義することはお勧めできません。基本クラスはコードを結合し、簡単に変更できないようにするためです。継承よりも構成を使用することをお勧めします。
このAPIの範囲では、基本クラスを使用することは実際には問題ではありません。サービスはあまり成長しません。サービスまたはアプリケーションが頻繁に成長および変更することに気付いた場合は、基本クラスの使用を避けてください。

次に、同じフォルダーにSaveCategoryResponseという新しいクラスを追加します。

応答タイプは、Categoryプロパティも設定します。このプロパティには、リクエストが正常に終了した場合にカテゴリデータが含まれます。

このクラスに3つの異なるコンストラクターを定義していることに注意してください。

  • プライベートパラメータ。成功パラメータとメッセージパラメータを基本クラスに渡し、Categoryプロパティも設定します。
  • カテゴリーをパラメーターとしてのみ受け取るコンストラクター。これは成功した応答を作成し、プライベートコンストラクターを呼び出してそれぞれのプロパティを設定します。
  • メッセージのみを指定する3番目のコンストラクター。これは、失敗応答を作成するために使用されます。

C#は複数のコンストラクターをサポートしているため、異なるコンストラクターを使用するだけで、これを処理するための異なるメソッドを定義せずに、応答の作成を単純化しました。

これで、サービスインターフェイスを変更して、新しいsaveメソッドコントラクトを追加できます。

ICategoryServiceインターフェイスを次のように変更します。

このメソッドにカテゴリを渡すだけで、モデルデータを保存するために必要なすべてのロジックを処理し、リポジトリやその他の必要なサービスを調整します。

このタスクを実行するために他のパラメーターは必要ないため、ここでは特定のリクエストクラスを作成していません。 KISSと呼ばれるコンピュータープログラミングの概念があります-Keep it Simple、Stupidの略です。基本的に、アプリケーションをできるだけシンプルに保つ必要があると書かれています。

アプリケーションを設計するときは、これを覚えておいてください。問題を解決するために必要なものだけを適用してください。アプリケーションを過剰に設計しないでください。

これで、エンドポイントロジックを終了できます。

要求データを検証し、リソースをモデルにマッピングした後、それをサービスに渡してデータを永続化します。

何かが失敗すると、APIは不正なリクエストを返します。そうでない場合、APIは新しいカテゴリ(新しいIdなどのデータを含む)を以前に作成したCategoryResourceにマップし、クライアントに送信します。

次に、サービスの実際のロジックを実装しましょう。

ステップ13 —データベースロジックと作業単位パターン

データベースにデータを永続化するため、リポジトリに新しいメソッドが必要です。

ICategoryRepositoryインターフェイスに新しいAddAsyncメソッドを追加します。

それでは、実際のリポジトリクラスでこのメソッドを実装しましょう。

ここでは、セットに新しいカテゴリを追加するだけです。

クラスをDBSet <>に追加すると、EF Coreはモデルに発生するすべての変更の追跡を開始し、このデータを現在の状態で使用して、モデルを挿入、更新、または削除するクエリを生成します。

現在の実装ではモデルをセットに追加するだけですが、データは保存されません。

データベースへのクエリを実際に実行するために呼び出す必要のあるコンテキストクラスにSaveChangesというメソッドがあります。リポジトリはデータを永続化すべきではなく、メモリ内のオブジェクトのコレクションであるため、ここでは呼び出しません。

このテーマは、経験豊富な.NET開発者の間でも非常に議論の余地がありますが、リポジトリクラスでSaveChangesを呼び出さない理由を説明します。

リポジトリは、.NETフレームワークに存在する他のコレクションと概念的に考えることができます。 .NET(およびJavascriptやJavaなどの他の多くのプログラミング言語)のコレクションを扱う場合、一般的に次のことができます。

  • リストに新しいアイテムを追加します(データをリスト、配列、辞書にプッシュするときなど)。
  • アイテムを検索またはフィルターします。
  • コレクションからアイテムを削除します。
  • 特定のアイテムを置き換えるか、更新します。

現実世界のリストを考えてください。スーパーで買い物をするための買い物リストを書いていると想像してみてください(偶然でしょう?)。

リストには、購入する必要があるすべての果物を書きます。このリストに果物を追加したり、購入を放棄した場合は果物を削除したり、果物の名前を置き換えたりすることができます。ただし、果物をリストに保存することはできません。そのようなことを平易な英語で言うのは意味がありません。

ヒント:オブジェクト指向プログラミング言語でクラスとインターフェースを設計するときは、自然言語を使用して、実行していることが正しいかどうかを確認してください。
たとえば、男性が人のインターフェースを実装すると言うのは理にかなっていますが、男性がアカウントを実装すると言うのは意味がありません。

果物リストを「保存」したい場合(この場合、すべての果物を購入するため)、支払いを行い、スーパーマーケットは在庫データを処理して、プロバイダーから果物をさらに購入する必要があるかどうかを確認します。

プログラミング時に同じロジックを適用できます。リポジトリはデータを保存、更新、削除すべきではありません。代わりに、このロジックを処理するために別のクラスに委任する必要があります。

データをリポジトリに直接保存する場合、別の問題があります。トランザクションを使用できません。

アプリケーションに、ユーザー名とAPIデータに変更が加えられるたびに実行されるアクションを保存するロギングメカニズムがあることを想像してください。

ここで、何らかの理由で、ユーザー名を更新するサービスへの呼び出しがあると想像してください(一般的なシナリオではありませんが、考えてみましょう)。

架空のユーザーテーブルのユーザー名を変更するには、最初にすべてのログを更新して、その操作を実行したユーザーを正しく確認する必要があることに同意しますか?

ここで、異なるリポジトリのユーザーとログにupdateメソッドを実装し、両方がSaveChangesを呼び出すことを想像してください。更新プロセスの途中でこれらの方法のいずれかが失敗するとどうなりますか?データの不整合が発生します。

すべてが終了した後にのみ、変更をデータベースに保存する必要があります。これを行うには、トランザクションを使用する必要があります。これは、基本的に、ほとんどのデータベースが複雑な操作が終了した後にのみデータを保存するために実装する機能です。

「-OK

この問題を処理する一般的なパターンは、作業単位パターンです。このパターンは、AppDbContextインスタンスを依存関係として受け取り、トランザクションを開始、完了、または中止するメソッドを公開するクラスで構成されます。

ここで問題に取り組むために、作業単位の単純な実装を使用します。

IUnitOfWorkと呼ばれるドメインレイヤーのRepositoriesフォルダー内に新しいインターフェイスを追加します。

ご覧のとおり、データ管理操作を非同期的に完了するメソッドのみを公開しています。

ここで実際の実装を追加しましょう。

PersistenceレイヤーのRepositoriesRepositoriesフォルダーにUnitOfWorkという新しいクラスを追加します。

これは、リポジトリを使用してデータベースの変更を完了した後にのみ、すべての変更をデータベースに保存する、シンプルでクリーンな実装です。

作業単位パターンの実装を調査すると、ロールバック操作を実装するより複雑なものが見つかります。

EF Coreは、すでにリポジトリパターンと作業単位をバックグラウンドで実装しているため、ロールバックメソッドを気にする必要はありません。

" - 何?では、なぜこれらすべてのインターフェイスとクラスを作成する必要があるのですか?」

永続性ロジックをビジネスルールから分離すると、コードの再利用性とメンテナンスに関して多くの利点が得られます。 EF Coreを直接使用すると、変更がそれほど簡単ではない、より複雑なクラスになります。

将来、ORMフレームワークを別のフレームワーク(Dapperなど)に変更したり、パフォーマンスのためにプレーンなSQLクエリを実装する必要がある場合を想像してください。クエリロジックをサービスに結合すると、多くのクラスでロジックを変更する必要があるため、ロジックを変更するのが難しくなります。

リポジトリパターンを使用すると、単純に新しいリポジトリクラスを実装し、依存関係注入を使用してバインドできます。

したがって、基本的に、EF Coreをサービスに直接使用し、何かを変更する必要がある場合、それが得られます。

前にも言ったように、EF Coreは、背後で作業単位とリポジトリのパターンを実装しています。 DbSet <>プロパティをリポジトリと見なすことができます。また、SaveChangesは、すべてのデータベース操作が成功した場合にのみデータを保持します。

作業単位とは何か、そしてリポジトリでそれを使用する理由がわかったので、実際のサービスのロジックを実装しましょう。

分離されたアーキテクチャのおかげで、UnitOfWorkのインスタンスをこのクラスの依存関係として単純に渡すことができます。

私たちのビジネスロジックはとてもシンプルです。

まず、新しいカテゴリをデータベースに追加しようとし、次にAPIがそれを保存しようとし、try-catchブロック内にすべてをラップします。

何かが失敗した場合、APIは架空のログサービスを呼び出し、失敗を示す応答を返します。

プロセスが問題なく終了すると、アプリケーションは成功の応答を返し、カテゴリデータを送信します。シンプルでしょ?

ヒント:実際のアプリケーションでは、すべてを一般的なtry-catchブロック内にラップするのではなく、考えられるすべてのエラーを個別に処理する必要があります。
try-catchブロックを追加するだけでは、考えられるほとんどの失敗シナリオをカバーできません。実装エラー処理を必ず修正してください。

APIをテストする前の最後のステップは、作業単位インターフェースをそれぞれのクラスにバインドすることです。

この新しい行をStartupクラスのConfigureServicesメソッドに追加します。

services.AddScoped ();

それではテストしましょう!

ステップ14 — Postmanを使用してPOSTエンドポイントをテストする

dotnet runを使用してアプリケーションを再度起動します。

ブラウザを使用してPOSTエンドポイントをテストすることはできません。 Postmanを使用してエンドポイントをテストしましょう。 RESTful APIをテストするのに非常に便利なツールです。

Postmanを開き、イントロメッセージを閉じます。次のような画面が表示されます。

エンドポイントをテストするためのオプションを示す画面

デフォルトで選択されているGETを選択ボックスに変更してPOSTにします。

[リクエストURLの入力]フィールドにAPIアドレスを入力します。

APIに送信するリクエスト本文データを提供する必要があります。 Bodyメニュー項目をクリックし、その下に表示されるオプションをrawに変更します。

Postmanの右側に[テキスト]オプションが表示されます。 JSON(application / json)に変更し、以下のJSONデータを以下に貼り付けます。

{
  "名前": ""
}
リクエストを送信する直前の画面

ご覧のとおり、空の名前文字列を新しいエンドポイントに送信します。

[送信]ボタンをクリックします。次のような出力が表示されます。

ご覧のとおり、検証ロジックは機能します!

エンドポイント用に作成した検証ロジックを覚えていますか?この出力は、動作する証拠です!

右側に表示される400ステータスコードにも注意してください。 BadRequestの結果により、このステータスコードが自動的に応答に追加されます。

次に、JSONデータを有効なものに変更して、新しい応答を確認します。

最後に、期待される結果

APIは新しいリソースを正しく作成しました。

これまで、私たちのAPIはカテゴリをリストおよび作成できました。 C#言語、ASP.NET Coreフレームワーク、およびAPIを構成する一般的な設計アプローチについて多くのことを学びました。

カテゴリーを更新するエンドポイントを作成するカテゴリーAPIを続けましょう。

これからは、ほとんどの概念を説明したので、時間を無駄にしないために、説明をスピードアップして新しいテーマに焦点を当てます。行こう!

ステップ15 —カテゴリーを更新する

カテゴリを更新するには、HTTP PUTエンドポイントが必要です。

コーディングする必要があるロジックは、POSTに非常に似ています。

  • 最初に、ModelStateを使用して着信要求を検証する必要があります。
  • リクエストが有効な場合、APIはAutoMapperを使用して受信リソースをモデルクラスにマップする必要があります。
  • 次に、サービスを呼び出して、カテゴリを更新するように指示し、それぞれのカテゴリIDと更新されたデータを提供する必要があります。
  • 指定されたIDを持つカテゴリがデータベースにない場合、不正なリクエストを返します。代わりにNotFoundの結果を使用することもできますが、クライアントアプリケーションにエラーメッセージを提供するため、このスコープではそれほど問題ではありません。
  • 保存ロジックが正しく実行された場合、サービスは更新されたカテゴリデータを含む応答を返す必要があります。そうでない場合は、プロセスが失敗したことを示すメッセージと、その理由を示すメッセージを提供する必要があります。
  • 最後に、エラーがある場合、APIは不正なリクエストを返します。そうでない場合、更新されたカテゴリモデルをカテゴリリソースにマップし、クライアントアプリケーションに成功応答を返します。

新しいPutAsyncメソッドをコントローラークラスに追加しましょう。

POSTロジックと比較すると、ここで1つだけ違いがあることに気付くでしょう。HttPut属性は、指定されたルートが受け取るパラメーターを指定します。

/ api / categories / 1のような最後のURLフラグメントとしてカテゴリIDを指定して、このエンドポイントを呼び出します。 ASP.NET Coreパイプラインは、このフラグメントを同じ名前のパラメーターに解析します。

次に、UpdateAsyncメソッドシグネチャをICategoryServiceインターフェイスに定義する必要があります。

それでは、実際のロジックに移りましょう。

手順16 —更新ロジック

カテゴリを更新するには、まず、データベースから現在のデータが存在する場合、それを返す必要があります。また、DBSet <>に更新する必要があります。

ICategoryServiceインターフェースに2つの新しいメソッドコントラクトを追加しましょう。

データベースからカテゴリを非同期的に返すFindByIdAsyncメソッドとUpdateメソッドを定義しました。 EF Core APIはモデルを更新するために非同期メソッドを必要としないため、Updateメソッドは非同期ではないことに注意してください。

それでは、実際のロジックをCategoryRepositoryクラスに実装しましょう。

最後に、サービスロジックをコーディングできます。

APIはデータベースからカテゴリを取得しようとします。結果がnullの場合、カテゴリが存在しないことを示す応答を返します。カテゴリが存在する場合、新しい名前を設定する必要があります。

APIは、新しいカテゴリを作成するときなど、変更を保存しようとします。プロセスが完了すると、サービスは成功応答を返します。そうでない場合、ロギングロジックが実行され、エンドポイントはエラーメッセージを含む応答を受け取ります。

それではテストしてみましょう。まず、使用する有効なIDを持つ新しいカテゴリを追加しましょう。データベースにシードするカテゴリの識別子を使用できますが、この方法でAPIが正しいリソースを更新することを示したいと思います。

アプリケーションを再度実行し、Postmanを使用して、データベースに新しいカテゴリをPOSTします。

新しいカテゴリを追加して後で更新する

有効なIDを取得したら、POSTオプションをPUTに変更して選択ボックスに入れ、URLの最後にID値を追加します。 nameプロパティを別の名前に変更し、結果を確認するリクエストを送信します。

カテゴリデータが正常に更新されました

APIエンドポイントにGETリクエストを送信して、カテゴリ名を正しく編集したことを確認できます。

それが今のGETリクエストの結果です

カテゴリに実装する必要がある最後の操作は、カテゴリの除外です。 HTTP Deleteエンドポイントを作成してみましょう。

ステップ17 —カテゴリーを削除する

カテゴリを削除するためのロジックは、必要なほとんどのメソッドが以前に構築されているため、実装が非常に簡単です。

これらは、私たちのルートが機能するために必要な手順です。

  • APIは、サービスを呼び出して、カテゴリを削除するよう指示し、それぞれのIDを提供する必要があります。
  • 指定されたIDのカテゴリがデータベースにない場合、サービスはそれを示すメッセージを返します。
  • 削除ロジックが問題なく実行された場合、サービスは削除されたカテゴリデータを含む応答を返す必要があります。そうでない場合は、プロセスが失敗したことを示す情報と、潜在的なエラーメッセージが表示されます。
  • 最後に、エラーがある場合、APIは不正なリクエストを返します。そうでない場合、APIは更新されたカテゴリをリソースにマップし、クライアントに成功応答を返します。

新しいエンドポイントロジックを追加して始めましょう。

HttpDelete属性は、idテンプレートも定義します。

DeleteAsync署名をICategoryServiceインターフェイスに追加する前に、小さなリファクタリングを行う必要があります。

新しいサービスメソッドは、PostAsyncメソッドとUpdateAsyncメソッドの場合と同じように、カテゴリデータを含む応答を返す必要があります。この目的のためにSaveCategoryResponseを再利用できますが、この場合はデータを保存しません。

この要件を満たすために同じ形状の新しいクラスを作成しないようにするには、SaveCategoryResponseの名前をCategoryResponseに変更します。

Visual Studio Codeを使用している場合は、SaveCategoryResponseクラスを開き、マウスカーソルをクラス名の上に置き、[すべての発生を変更]オプションを使用してクラスの名前を変更できます。

すべてのファイルの名前を変更する簡単な方法

必ずファイル名も変更してください。

DeleteAsyncメソッドシグネチャをICategoryServiceインターフェイスに追加しましょう。

削除ロジックを実装する前に、リポジトリに新しいメソッドが必要です。

RemoveメソッドシグネチャをICategoryRepositoryインターフェイスに追加します。

void Remove(Category category);

そして、リポジトリクラスに実際の実装を追加します。

EF Coreでは、単にIDを渡すのではなく、削除するモデルを正しく理解するために、モデルのインスタンスをRemoveメソッドに渡す必要があります。

最後に、CategoryServiceクラスにロジックを実装しましょう。

ここに新しいものは何もありません。サービスはIDでカテゴリを見つけようとし、リポジトリを呼び出してカテゴリを削除します。最後に、作業ユニットはデータベースへの実際の操作を実行するトランザクションを完了します。

「-ねえ、でも各カテゴリーの製品はどうですか?エラーを避けるために、最初にリポジトリを作成して製品を削除する必要はありませんか?」

答えはノーです。 EF Coreの追跡メカニズムのおかげで、データベースからモデルをロードすると、フレームワークはモデルがどの関係を持っているかを認識します。削除すると、EF Coreは関連するすべてのモデルを再帰的に最初に削除する必要があることを認識します。

クラスをデータベーステーブルにマッピングするときにこの機能を無効にすることはできますが、このチュートリアルの範囲外です。この機能について学びたい場合は、こちらをご覧ください。

次に、新しいエンドポイントをテストします。次のように、アプリケーションを再度実行し、Postmanを使用してDELETE要求を送信します。

ご覧のとおり、APIは既存のカテゴリを問題なく削除しました

GETリクエストを送信することで、APIが正しく機能していることを確認できます。

結果として1つのカテゴリのみを受け取ります

カテゴリAPIが完成しました。次に、製品APIに移行します。

ステップ18 —製品API

これまで、すべての基本的なHTTP動詞を実装して、ASP.NET CoreでCRUD操作を処理する方法を学びました。製品APIを実装する次のレベルに進みましょう。

すべてのHTTP動詞について詳しく説明しませんが、これは網羅的だからです。このチュートリアルの最後の部分では、GETリクエストのみを取り上げ、データベースからデータをクエリする際に関連するエンティティを含める方法と、EUnitOfMeasurement列挙値に対して定義したDescription属性を使用する方法を示します。

ProductsControllerという名前の新しいコントローラーをControllersフォルダーに追加します。

ここで何かをコーディングする前に、製品リソースを作成する必要があります。

リソースをどのように表示するかを再度示して、記憶を更新します。

{
 [
  {
   「id」:1、
   「名前」:「砂糖」、
   「quantityInPackage」:1
   「unitOfMeasurement」:「KG」
   "カテゴリー": {
   「id」:3
   「名前」:「砂糖」
   }
  }、
  …//その他の製品
 ]
}

データベースのすべての製品を含むJSON配列が必要です。

JSONデータは、次の2つの点で製品モデルと異なります。

  • 測定単位はより短い方法で表示され、その略語のみが表示されます。
  • CategoryIdプロパティを含めずにカテゴリデータを出力します。

測定単位を表すには、enum型の代わりに単純な文字列プロパティを使用できます(ところで、JSONデータのデフォルトのenum型がないため、別の型に変換する必要があります)。

新しいリソースを形成する方法ができたので、作成しましょう。新しいクラスProductResourceをResourcesフォルダーに追加します。

次に、モデルクラスと新しいリソースクラスの間のマッピングを構成する必要があります。

マッピング構成は、他のマッピングに使用される構成とほぼ同じですが、ここではEUnitOfMeasurement列挙型から文字列への変換を処理する必要があります。

列挙型に適用されたStringValue属性を覚えていますか?次に、.NETフレームワークの強力な機能であるReflection APIを使用して、この情報を抽出する方法を示します。

Reflection APIは、メタデータの抽出と操作を可能にする強力なリソースセットです。多くのフレームワークとライブラリ(ASP.NET Core自体を含む)は、これらのリソースを使用して、舞台裏で多くのことを処理します。

次に、実際にどのように機能するかを見てみましょう。 EnumExtensionsという新しいクラスをExtensionsフォルダーに追加します。

コードを初めて見ると怖いかもしれませんが、それほど複雑ではありません。コード定義を分解して、その仕組みを理解しましょう。

まず、指定された列挙型を引数として受け取るジェネリックメソッド(この場合、TEnum宣言で表される複数のタイプの引数を受け取ることができるメソッド)を定義しました。

enumはC#の予約済みキーワードであるため、パラメーター名の前に@を追加して有効な名前にしました。

このメソッドの最初の実行手順は、GetTypeメソッドを使用してパラメーターの型情報(クラス、インターフェイス、列挙、または構造体定義)を取得することです。

次に、メソッドはGetField(@ enum.ToString())を使用して特定の列挙値(たとえば、キログラム)を取得します。

次の行は、列挙値に適用されたすべてのDescription属性を見つけ、そのデータを配列に格納します(場合によっては、同じプロパティに複数の属性を指定できます)。

最後の行では、列挙型の説明属性が少なくとも1つあるかどうかを確認するために、短い構文を使用しています。持っている場合、この属性によって提供されるDescription値を返します。そうでない場合は、デフォルトのキャストを使用して、列挙を文字列として返します。

? operator(null-conditional operator)は、プロパティにアクセスする前に値がnullかどうかをチェックします。

??演算子(nu​​ll合体演算子)は、空でない場合は左側の値を返し、そうでない場合は右側の値を返すようにアプリケーションに指示します。

説明を抽出する拡張メソッドができたので、モデルとリソース間のマッピングを構成しましょう。 AutoMapperのおかげで、たった1つの追加行でそれを行うことができます。

ModelToResourceProfileクラスを開き、次の方法でコードを変更します。

この構文は、新しい拡張メソッドを使用してEUnitOfMeasurement値をその説明を含む文字列に変換するようAutoMapperに指示します。シンプルでしょ?完全な構文を理解するには、公式ドキュメントを読むことができます。

categoryプロパティのマッピング構成を定義していないことに注意してください。以前にカテゴリのマッピングを構成し、製品モデルに同じタイプと名前のカテゴリプロパティがあるため、AutoMapperはそれぞれの構成を使用してマッピングする必要があることを暗黙的に認識します。

次に、エンドポイントコードを追加しましょう。 ProductsControllerコードを変更します。

基本的に、カテゴリーコントローラーに定義された同じ構造。

サービスの部分に行きましょう。ドメインレイヤーにあるサービスフォルダーに新しいIProductServiceインターフェイスを追加します。

新しいサービスを実際に実装する前に、リポジトリが必要であることに気付いているはずです。

IProductRepositoryという新しいインターフェイスをそれぞれのフォルダーに追加します。

それでは、リポジトリを実装しましょう。データをクエリするときに各製品のそれぞれのカテゴリデータを返す必要があることを除いて、カテゴリリポジトリの場合とほぼ同じ方法で実装する必要があります。

EF Coreは、デフォルトでは、データをクエリするときにモデルに関連するエンティティを含めません。データが非常に遅い可能性があるためです(10個の関連エンティティを持つモデルを想像してください。

カテゴリデータを含めるには、1行追加するだけで済みます。

Include(p => p.Category)の呼び出しに注意してください。この構文をチェーン化して、データのクエリ時に必要な数のエンティティを含めることができます。 EF Coreは、選択の実行時にそれを結合に変換します。

これで、カテゴリの場合と同じ方法でProductServiceクラスを実装できます。

Startupクラスを変更する新しい依存関係をバインドしましょう:

最後に、APIをテストする前に、AppDbContextクラスを変更して、アプリケーションを初期化するときにいくつかの製品を含めて、結果を確認できるようにします。

アプリケーションを初期化するときにシードするカテゴリに関連付ける2つの架空の製品を追加しました。

テストする時間です! APIを再度実行し、Postmanを使用してGETリクエストを/ api / productsに送信します。

ほら!こちらが当社の製品です

以上です!おめでとうございます!

これで、分離されたアーキテクチャを使用してASP.NET Coreを使用してRESTful APIを構築する方法の基礎ができました。 .NET Coreフレームワークの多くのこと、C#の操作方法、EF CoreとAutoMapperの基本、アプリケーションの設計時に使用する多くの有用なパターンを学びました。

製品のその他のHTTP動詞を含むAPIの完全な実装を確認し、Githubリポジトリを確認できます。

結論

ASP.NET Coreは、Webアプリケーションを作成するときに使用する優れたフレームワークです。クリーンで保守可能なアプリケーションを構築するために使用できる多くの便利なAPIが付属しています。プロフェッショナルなアプリケーションを作成する際のオプションとして検討してください。

この記事では、プロフェッショナルなAPIのすべての側面を取り上げたわけではありませんが、すべての基本を学びました。また、私たちが毎日直面するパターンを解決するための多くの有用なパターンを学びました。

この記事をお楽しみいただき、お役に立てば幸いです。これを改善する方法を理解するために、あなたのフィードバックに感謝します。

学習を続けるための参照

.NET Coreチュートリアル— Microsoft Docs

ASP.NET Coreドキュメント— Microsoft Docs