HTTP サーバーハンドラ
このページは英語から自動翻訳されています。 原文を読む
Sisk バージョン 0.16 では、HttpServerHandler クラスを導入しました。このクラスは、Sisk の全体的な動作を拡張し、Http リクエスト、ルーター、コンテキストバッグなどの追加イベントハンドラを提供することを目的としています。
このクラスは、HTTP サーバー全体および個々のリクエストのライフタイム中に発生するイベントを集中管理します。Http プロトコルにはセッションが存在しないため、リクエスト間で情報を保持することはできません。現在の Sisk では、セッション、コンテキスト、データベース接続、その他の便利なプロバイダーを実装できるようにしています。
各イベントがいつトリガーされ、何のためにあるかについては、this page を参照してください。また、リクエストで何が起こり、どこでイベントが発火するかを理解するために、lifecycle of an HTTP request もご覧ください。HTTP サーバーは複数のハンドラを同時に使用できます。各イベント呼び出しは同期的であり、すべてのハンドラが実行・完了するまで、リクエストまたはコンテキストごとに現在のスレッドがブロックされます。
RequestHandlers とは異なり、これらは特定のルートグループや個別のルートに適用できません。代わりに、HTTP サーバー全体に適用されます。Http Server Handler 内で条件を設定することも可能です。さらに、各 HttpServerHandler のシングルトンはすべての Sisk アプリケーションで定義されるため、HttpServerHandler ごとにインスタンスは 1 つだけです。
HttpServerHandler を使用する実用的な例として、リクエストの終了時にデータベース接続を自動的に破棄する方法があります。
// DatabaseConnectionHandler.cs
public class DatabaseConnectionHandler : HttpServerHandler
{
protected override void OnHttpRequestClose(HttpServerExecutionResult result)
{
var requestBag = result.Request.Context.RequestBag;
// リクエストが DbContext を定義しているか確認する
// そのコンテキストバッグ内で
if (requestBag.IsSet<DbContext>())
{
var db = requestBag.Get<DbContext>();
db.Dispose();
}
}
}
public static class DatabaseConnectionHandlerExtensions
{
public static DbContext GetDbContext(this HttpRequest request)
{
return request.Bag.GetOrAdd(() => new DbContext());
}
}上記のコードでは、GetDbContext 拡張メソッドにより、HttpRequest オブジェクトから直接接続コンテキストを作成できるようになります。未破棄の接続はデータベース操作時に問題を引き起こす可能性があるため、OnHttpRequestClose で確実に終了させます。
Http サーバーにハンドラを登録するには、ビルダー内または直接 HttpServer.RegisterHandler を使用します。
// Program.cs
class Program
{
static void Main(string[] args)
{
using var app = HttpServer.CreateBuilder()
.UseHandler<DatabaseConnectionHandler>()
.Build();
app.Router.MapInstance(new UserController());
app.Start();
}
}これにより、UsersController クラスは次のようにデータベースコンテキストを利用できます。
// UserController.cs
[RoutePrefix("/users")]
public class UserController : ApiController
{
[RouteGet()]
public async Task<HttpResponse> List(HttpRequest request)
{
var db = request.GetDbContext();
var users = db.Users.ToArray();
return JsonOk(users);
}
[RouteGet("<id>")]
public async Task<HttpResponse> View(HttpRequest request)
{
var db = request.GetDbContext();
int userId = request.RouteParameters["id"].GetInteger();
var user = db.Users.FirstOrDefault(u => u.Id == userId);
return JsonOk(user);
}
[RoutePost]
public async Task<HttpResponse> Create(HttpRequest request)
{
var db = request.GetDbContext();
var user = await request.GetJsonContentAsync<User>();
ArgumentNullException.ThrowIfNull(user);
db.Users.Add(user);
await db.SaveChangesAsync();
return JsonMessage("User added.");
}
}上記のコードは、RouterController から継承された ApiController に組み込まれている JsonOk や JsonMessage といったメソッドを使用しています。
// ApiController.cs
public class ApiController : RouterModule
{
public HttpResponse JsonOk(object value)
{
return new HttpResponse(200)
.WithContent(JsonContent.Create(value, null, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
}));
}
public HttpResponse JsonMessage(string message, int statusCode = 200)
{
return new HttpResponse(statusCode)
.WithContent(JsonContent.Create(new
{
Message = message
}));
}
}ハンドラでスローされる例外 #
Sisk 1.7 以降、サーバーハンドラでスローされた例外は以下のように処理されます。
OnServerStarting、OnServerStarted、OnSetupRouterでスローされた例外はサーバーの起動を中止します。例外はInvalidOperationExceptionでラップされ、元の例外はInnerExceptionに格納されます。- その他のイベントでスローされた例外は ErrorsLogsStream に書き込まれ、
ThrowExceptionsが有効化されていない限り残りのハンドラは引き続き呼び出されます。ThrowExceptionsが有効な場合、例外はInvalidOperationExceptionでラップされます。 OnHttpRequestCloseやOnException、またはアクセスログ・エラーログのフォーマット中にスローされた例外は、ThrowExceptionsが有効でも HTTP エンジンに伝播せず、ErrorsLogsStreamに書き込まれます。過去のバージョンでは、これらの例外がプロセスを終了させたり、接続を開いたままにしたりすることがありました。
開発者はこのクラスを使用してセッション、コンテキスト、データベース接続を実装できます。提示したコードは、DatabaseConnectionHandler を用いた実用的な例で、リクエスト終了時にデータベース接続を自動的に破棄する方法を示しています。
ハンドラはサーバー設定時に登録するだけで簡単に統合でき、HttpServerHandler クラスはリソース管理と Sisk の HTTP アプリケーションにおける動作拡張のための強力なツールセットを提供します。