序文
動的Web API(Dynamic Web API)という言葉に触れたのは数年前のことです。当時、ABPフレームワークでこの技術に興味を持ち、ABPのコードを分析し、独立したコンポーネントとして利用する試みもしましたが、ABPへの依存が多すぎたため断念しました。十数日前、友人の熊猫氏がこのコードをABPから正常に分離し、シンプルなデモを作成してくれました。長い間(本当に怠けていました)このコードを修正し、機能を追加し、パッケージ化した結果、現在では独立したコンポーネントとして利用できるようになりました。プロジェクトはGitHubでオープンソース化されています(https://github.com/dotnetauth/Panda.DynamicWebApi)。役に立ったと思われる方はStarをいただけると幸いです。
本記事では使用方法についてのみ説明し、原理の詳細は後の記事で解説します。
紹介
伝統的な3層アーキテクチャ、DDDの古典的な4層アーキテクチャ(DDD Lite)、またはその他のアプリケーションロジック層(ビジネスロジック層)を持つアーキテクチャであっても、Webアプリケーション開発において、私たちのビジネスロジックは最終的にWeb APIを通じて呼び出される必要があります。ここで、私たちは繰り返しの操作に直面することがあります:ビジネスロジックの記述 -> APIの記述。このような繰り返しの操作を解決する方法はありませんか?ビジネスロジックを記述した後、自動的にWeb APIを生成してくれれば理想的です。その答えはもちろんあります。
ここで本記事の主役を紹介します:`Panda.DynamicWebApi`(https://github.com/dotnetauth/Panda.DynamicWebApi)。ABPから派生した、独立して利用可能で、ビジネスロジック層に基づいてASP.NET Core Web API層を自動生成するオープンソースコンポーネントです。生成されるAPIはRESTfulスタイルに準拠しており、条件を満たすクラスに基づいてWeb APIを生成します。MVCフレームワークが直接ロジックを呼び出すため、パフォーマンスの問題はなく、Swaggerと完璧に連携してAPIドキュメントを構築できます。
使用方法
ここでは、DDDの古典的な4層アーキテクチャにおけるアプリケーションロジック層を例に説明します。
1. 準備
(1) 2つのプロジェクトを作成します。1つはアプリケーションロジック層のクラスライブラリプロジェクト、もう1つはWeb APIホストとなるASP.NET Core Web APIプロジェクトです。
(2) アプリケーションロジックの記述
アプリケーションロジックインターフェースを定義します。すべてのアプリケーションロジックはこれを実装する必要があります:
public interface IBaseService
{
}
学生管理ロジックインターフェースを定義し、アプリケーションロジックインターフェースを継承します。
public interface IStudentService : IBaseService
{
/// <summary>
/// IDで学生を取得します
/// </summary>
/// <param name="studentId"></param>
/// <returns></returns>
StudentDto Get(int studentId);
/// <summary>
/// すべての学生を取得します
/// </summary>
/// <returns></returns>
List<StudentDto> GetAll();
/// <summary>
/// 学生情報を更新します
/// </summary>
/// <param name="input"></param>
void Update(UpdateStudentDto input);
/// <summary>
/// 学生の年齢を更新します
/// </summary>
/// <param name="age"></param>
[HttpPatch("{id:int}/age")]
void UpdateAge(int age);
/// <summary>
/// IDで学生を削除します
/// </summary>
/// <param name="studentId"></param>
[HttpDelete("{id:int}")]
void Delete(int studentId);
/// <summary>
/// 学生を追加します
/// </summary>
/// <param name="input"></param>
void Create(CreateStudentDto input);
}
学生ロジック管理インターフェースを実装します:
public class StudentService : IStudentService
{
/// <summary>
/// IDで学生を取得します
/// </summary>
/// <param name="studentId"></param>
/// <returns></returns>
[HttpGet("{id:int}")]
public StudentDto Get(int studentId)
{
return new StudentDto() { Id = 101, Age = 20, Name = "田中" };
}
/// <summary>
/// すべての学生を取得します
/// </summary>
/// <returns></returns>
public List<StudentDto> GetAll()
{
return new List<StudentDto>()
{
new StudentDto(){ Id = 101, Age = 20, Name = "田中" },
new StudentDto(){ Id = 102, Age = 21, Name = "鈴木" }
};
}
/// <summary>
/// 学生情報を更新します
/// </summary>
/// <param name="input"></param>
public void Update(UpdateStudentDto input)
{
throw new System.NotImplementedException();
}
/// <summary>
/// 学生の年齢を更新します
/// </summary>
/// <param name="age"></param>
[HttpPatch("{id:int}/age")]
public void UpdateAge(int age)
{
throw new System.NotImplementedException();
}
/// <summary>
/// IDで学生を削除します
/// </summary>
/// <param name="studentId"></param>
[HttpDelete("{id:int}")]
public void Delete(int studentId)
{
throw new System.NotImplementedException();
}
/// <summary>
/// 学生を追加します
/// </summary>
/// <param name="input"></param>
public void Create(CreateStudentDto input)
{
throw new System.NotImplementedException();
}
}
(3) Web APIホストプロジェクトにSwaggerを設定します。
Install-Package Swashbuckle.AspNetCore
Startupでの設定
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "学生管理システム WebApi", Version = "v1" });
c.DocumentFilter<AllDocumentFilter>();
c.IncludeXmlComments(@"bin\Debug
etcoreapp3.1\Xc.StuMgr.WebApiHost.xml");
c.IncludeXmlComments(@"bin\Debug
etcoreapp3.1\Xc.StuMgr.Application.xml");
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "学生管理システム WebApi");
});
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
実行すると、デフォルトのValuesControllerの5つのAPIが表示されます。
2. 動的Web API
NuGetを通じてApplicationプロジェクトにコンポーネントをインストールします:
Install-Package Panda.DynamicWebApi
インターフェース `IBaseService` に `IDynamicWebApi` を継承させ、特性 `[DynamicWebApi]` を追加します。
[DynamicWebApi]
public interface IBaseService : IDynamicWebApi
{
}
Web APIホストプロジェクトのStartupで動的Web APIを設定します:
// 動的Web APIを追加するには、AddControllersの後に配置する必要があります
services.AddDynamicWebApi();
ブラウザを開いてアクセスすると、以下のようになります:
StudentServiceのWeb APIが正常に生成され、Swaggerと完璧に連携していることが確認できます。
詳細な説明
上記の説明から、使用方法は非常に簡単であることがわかります。2つのステップだけです:
ステップ1:クラス(またはそのインターフェース、継承する抽象クラス、ただし親クラスには配置しない)に `IDynamicWebApi` を継承させ、特性 `[DynamicWebApi]` を追加します。
ステップ2:Startupで登録します。
// 動的Web APIを追加するには、AddControllersの後に配置する必要があります
services.AddDynamicWebApi();
MVCのクラスを使用して処理する必要があるため、AddControllersの後に配置する必要があります。このコンポーネントにはチェック機能があります。
1. ルール
このコンポーネントは「規約優先」を採用しているため、実際の使用においていくつかのルールがあります:
(1) クラスが動的APIを生成するためには、2つの条件を満たす必要があります。1つはそのクラスが `IDynamicWebApi` を**直接**または**間接的に**実装していること、もう1つはそのクラス**自体**または**親の抽象クラス**または**実装するインターフェース**が特性 `DynamicWebApi` を持っていることです。
(2) 特性 `[NonDynamicWebApi]` を追加すると、クラスまたはメソッドがAPIを生成しないようにできます。`[NonDynamicWebApi]` は最も高い優先順位を持ちます。
(3) 規則に合致する動的APIの**クラス名**から接尾辞が削除されます。例えば、前述の `StudentService` からは、Service接尾辞が削除されます。このルールは動的に設定できます。
(4) APIルートプレフィックスが自動的に追加されます。デフォルトでは、すべてのAPIに `api` プレフィックスが追加されます。
(5) デフォルトのHTTP動詞は `POST` です。`HttpGet`/`HttpPost`/`HttpDelete` などのASP.NET Coreの組み込み特性で上書きできます。
(6) `HttpGet`/`HttpPost`/`HttpDelete` などの組み込み特性でデフォルトのルートを上書きできます。
(7) デフォルトでは、メソッド名に基づいてHTTP動詞が設定されます。例えば、CreateAppleまたはCreateは `POST` 動詞のAPIを生成します。以下の対応表に一致する場合(大文字小文字を無視)、そのAPI名のこの動詞部分が省略されます。例えば、CreateAppleはAppleになります。以下の対応表にない場合は、デフォルトの動詞 `POST` が使用されます。
| メソッド名の先頭 | 動詞 |
|---|---|
| create | POST |
| add | POST |
| post | POST |
| get | GET |
| find | GET |
| fetch | GET |
| query | GET |
| update | PUT |
| put | PUT |
| delete | DELETE |
| remove | DELETE |
(8) メソッド名はパスカルケース(PascalCase)を使用することを強く推奨します。これにより、API名の自動処理がより適切に行われ、上記の対応表の動詞を使用しやすくなります。例:
リンゴを追加 -> Add/AddApple/Create/CreateApple
リンゴを更新 -> Update/UpdateApple
...
(9) `[DynamicWebApi]` 特性は継承可能であるため、親クラスが誤って識別されないように、抽象クラスまたはインターフェース以外の親クラスには配置しないでください。
2. 設定
すべての設定は `DynamicWebApiOptions` オブジェクト内にあります。以下に説明します:
| プロパティ名 | 必須か | 説明 |
|---|---|---|
| DefaultHttpVerb | いいえ | デフォルト値:POST。デフォルトのHTTP動詞 |
| DefaultAreaName | いいえ | デフォルト値:空。Areaルート名 |
| DefaultApiPrefix | いいえ | デフォルト値:api。APIルートプレフィックス |
| RemoveControllerPostfixes | いいえ | デフォルト値:AppService/ApplicationService。クラス名から削除する接尾辞 |
| RemoveActionPostfixes | いいえ | デフォルト値:Async。メソッド名から削除する接尾辞 |
| FormBodyBindingIgnoredTypes | いいえ | デフォルト値:IFormFile。MVCがパラメータリストにバインドしない型。 |
トラブルシューティング
問題が発生した場合は、Issuesで質問してください。
終わり
プロジェクトのオープンソースアドレス:https://github.com/dotnetauth/Panda.DynamicWebApi。Starをいただけると幸いです。
本記事のデモアドレス:XiaoChen.StudentManagement
ABP:https://github.com/aspnetboilerplate/aspnetboilerplate