こんにちは。MLBお兄さんこと松村です。 MLB も開幕から1ヶ月経過しましたが、既にノーヒットノーランが4度も達成されています!
GW に個人的な制作として NuGet パッケージをリリースしました。
www.nuget.org
今回の記事は ASP.NET Core のカスタムミドルウェアを作るとき、また、パッケージとして配布するときに意識することをまとめます。
基本的なことはドキュメントに書いてあるので、ぜひ参考にしましょう。
ターゲットフレームワークを決める
C# のプロジェクトを作るときに、まず考えるのはどのバージョンに対応するかです。 (Target Framework Monikers / TFM といいます)
.NET Framework 向けなのか、.NET Core 向けなのか、どちらもなのか、というところです。
ライブラリとして利用する場合は基本的に SDK 形式になると思います。プロジェクトファイル (.csproj) でいうと <Project Sdk="Microsoft.NET.Sdk">
で始まる形式です。
そして .NET Standard を使って実装することが多いと思うので、このあたりが参考になるでしょう。
また csproj 関連で言えば GeneratePackageOnBuild
属性を設定しておくことで、ビルドのたびにパッケージファイルを生成することができます。
パイプライン前後のどちらで実行するかを決める
ミドルウェアの特徴としては HTTP リクエストのパイプラインの途中に処理を差し込めることです。(画像引用元)
実装する際は、HTTP リクエストのはじめに実行する処理なのか、あとに実行する処理なのかを考えましょう。
どちらになるかによって next
と呼ばれる、次のパイプラインを呼び出すデリゲートの実行タイミングが変わります。
逆にいうとパイプラインを途中で止める(ぶった切る)ミドルウェアを作りたいなら next
デリゲートを実行しなければ良いです。
public class CustomMiddleware { public CustomMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext) { // HTTP リクエストのはじめに実行するタイミング doSomething(); // 次のパイプラインを実行する await _next(httpContext); // HTTP リクエストのあとに実行するタイミング doSomething(); } }
Dependency Injection をきちんと使う
ミドルウェアからも DI コンテナーにアクセスすることができます。必要なリソースは DI を通じて取得するようにしましょう。
Invoke
メソッドの引数に指定しておくことで、取得することができます。
例えば私が作ったパッケージでは IConfiguration
が必要なので、このように書いています。
public async Task Invoke(HttpContext httpContext, IConfiguration configuration)
{
...
}
呼出用の拡張メソッドを提供する
ミドルウェアを使うには当然呼び出してあげなければいけません。ASP.NET Core の場合は Startup.cs の Configure
メソッドに書きます。
ライブラリとしてミドルウェアを提供する場合、呼出用の拡張メソッドも一緒に実装しましょう。
namespace Microsoft.AspNetCore.Builder { public static class CustomMiddlewareBuilderExtensions { public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder app) { return app.UseMiddleware<CustomMiddleware>(); } } }
このように書いておけば、Startup.cs で他のミドルウェアと同じような書き方に合わせることができます。
public class Startup { public void Configure(IApplicationBuilder app) { // 呼出用の拡張メソッドを使う場合 app.UseCustomMiddleware(); // 呼出用の拡張メソッドを使わない場合 app.UseMiddleware<CustomMiddleware>(); } }
また、ミドルウェアに対してオプションを指定したいなら、オプションクラスを用意して、変数として渡すメソッドと、直接書けるメソッドを用意しておくと便利です。
public class Startup { public void Configure(IApplicationBuilder app) { // オプション変数を渡す場合 CustomMiddlwareOptions options = new CustomMiddlwareOptions { Aaa = ..., Bbb = ... }; app.UseCustomMiddleware(options); // オプションを直接書く場合 app.UseCustomMiddleware(options => { options.Aaa = ...; options.Bbb = ...; }); } }
テストを書く
当然テストは書きましょう。ミドルウェアは HttpContext を扱うため、若干の難しさがあります。(モックとか)
テストの書き方についてはドキュメントが揃っているので、参考にしましょう。
DI 経由で使用するオブジェクトについては、 Moq などのモッキングツールを使ってテストを書くと良いでしょう。
README を書く
ミドルウェアに関わらずパッケージを公開する場合、そのパッケージの使い方や注意点が分かるようにしましょう。
そういった情報は README に書いておくと良いです。ちなみに私は Swashbuckle.AspNetCore の REAMDE を参考にしました。
README を書いておくことで nuget.org に公開したときに、パッケージのページに README の内容が表示されるようになるそうです。
CI/CD を準備する
ミドルウェア関係なくなって来ましたが、CI/CD を準備しておくとパッケージの公開が楽になります。
GitHub Actions での方法は試していませんが、 Azure Pipelines なら公開するためのタスクが用意されています。
nuget.org にパッケージをプッシュする場合は APIキーが必要になります。
APIキーは nuget.org で発行することができ、Azure DevOps の Service Connection に登録することで使用できるようになります。
このあたりが、カスタムミドルウェアを NuGet パッケージで公開するときに意識したことになります。