HttpContext Request Features
V ASP.NET Core sme zvyknutí pracovať s dependency injection a zvykneme väčšinu informácií prenášať práve cez injektovanie závislostí do tried kde ich potrebujeme. Niekedy však pracujeme s informáciami, ktoré sú úzko späté s konkrétnym requestom a nepotrebujeme, nechceme alebo nemôžeme ich injektovať do tried ako závislosti. Pre takéto prípady je tu HttpContext.Features
.
Vlastnosť Features
je jednoduchá kolekcia typu IFeatureCollection
, do ktorej si môžeme ukladať silne typové objektu a následne na miestach kde ich potrebujeme získavať. Táto kolekcia je dostupná v rámci HttpContext
a je dostupná počas celého životného cyklu requestu. Sám ASP.NET Core framework využíva túto kolekciu na ukladanie rôznych informácii, napríklad:
IRouteValuesFeature
- obsahuje informácie o parametroch a ich hodnotách z cesty dotazuIEndpointFeature
- obsahuje informácie o koncovom endpointe, ktorý spracováva request- ...
Vlastnosť Features
je dostupná aj pre nás a môžeme si do nej ukladať vlastné informácie, ktoré budeme potrebovať počas spracovania requestu. Príklad použitia môže byť napríklad ukladanie informácii o aktuálnom používateľovi, aktuálnom tenantovi alebo iných informácii, ktoré sú potrebné pre spracovanie requestu s sú určitým spôsobom viazané, respektíve získavané zo samotného requestu.
Informácie sa do Features
zvyčajne ukladajú v rámci middleware, ktorý je zaregistrovaný v rámci pipeline aplikácie. Middleware môže získavať informácie z requestu, spracovať ich a následne ich uložiť do Features
pre ďalšie spracovanie v rámci aplikácie.
Príklad middleware, ktorý získava informácie o aktuálnom tenantovi z requestu a ukladá ich do Features
môže vyzerať nasledovne:
public class CurrentTenantInfoMiddleware(RequestDelegate next)
{
private readonly RequestDelegate _next = next;
public async Task InvokeAsync(HttpContext context, ITenantService tenantService)
{
// 👇 More complex logic to determine the current tenant can be added here
if (context.Request.Headers.TryGetValue("X-TenantId", out var routeValue)
&& Guid.TryParse(routeValue.First()?.ToString(), out var tenantId))
{
var tenantInfo = await tenantService.GetTenantInfoAsync(tenantId);
context.Features.Set(tenantInfo);
}
await _next(context);
}
}
// 👇 Register the middleware
app.UseMiddleware<CurrentTenantInfoMiddleware>();
Následne môžeme v rámci ďalších častí aplikácie pristupovať k informáciám o aktuálnom tenantovi z Features
:
app.MapGet("/api/", (HttpContext context) =>
{
// 👇 Access the current tenant info from the context
var currentTenantInfo = context.Features.Get<ICurrentTenantInfo>();
return Results.Ok(currentTenantInfo?.Id);
});
Pre zjednodušenie si môžeme vytvoriť extension metódu pre IFeatureCollection
:
public static class IFeatureCollectionExtensions
{
public static ICurrentTenantInfo? GetTenant(this IFeatureCollection features)
{
return features.Get<ICurrentTenantInfo>();
}
}
var tenantInfo = context.Features.GetTenant();
Pre úplnosť: ďalšia možnosť ako si prenášať informácie počas jedného requestu je použitie
HttpContext.Items
. Táto kolekcia je dostupná v rámciHttpContext
a je dostupná počas celého životného cyklu requestu. Avšak,Items
nie je silne typový. Je to kľúč hodnota. Preto je vhodnejšie použiťFeatures
ak potrebujeme ukladať silne typové objekty.