[英]Mixing dependency injection and manually-passed parameter in ASP.NET Core middleware constructor
我正在為 ASP.NET Core 2.2 編寫自定義中間件。 根據關於編寫自定義中間件的Microsoft Docs :
中間件組件可以通過構造函數參數從依賴注入(DI)中解析它們的依賴關系。
UseMiddleware<T>
也可以直接接受額外的參數。
這看起來都很好,但它並沒有說明當我混合這兩種方式時會發生什么,例如使用 DI 並在UseMiddleware<T>
傳遞參數。 例如,我有以下中間件:
public class CustomMiddleware
{
public CustomMiddleware(RequestDelegate next, ILogger<CustomMiddleware> logger, CustomMiddlewareOptions options)
{
...
}
public async Task InvokeAsync(HttpContext context)
{
...
}
其中logger
由 DI 提供, options
提供如下:
app.UseMiddleware<CustomMiddleware>(new CustomMiddlewareOptions());
我自己對 2.2 的測試似乎表明這可以正常工作,並且構造函數中參數的順序無關緊要(我可以將 DI 參數放在手動傳遞的參數之前或之后,甚至在兩個手動傳遞的參數之間) . 但我正在尋找一些保證,即我所做的一切都沒有問題。 如果有人能指出一些支持這種用法的文檔或源代碼,那就太好了。 謝謝!
我自己對 2.2 的測試似乎表明這可以正常工作,並且構造函數中參數的順序無關緊要(我可以將 DI 參數放在手動傳遞的參數之前或之后,甚至在兩個手動傳遞的參數之間) . 但我正在尋找一些保證
是的。 閱讀源代碼后,我會說這很好。
你的CustomMiddleware
是一個約定俗成的中間件(不同於基於工廠的中間件),它由ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs) 激活:
var ctorArgs = new object[args.Length + 1];
ctorArgs[0] = next;
Array.Copy(args, 0, ctorArgs, 1, args.Length); //
var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);
這里的args
(Given Arguments) 是您傳遞給UseMiddleware<CustomMiddleware>(args)
(沒有next
)的參數數組。
准備構造函數參數時有兩個階段:
args
與構造參數類型匹配。 並在類型匹配時設置值。 在此處查看源代碼ServiceProvider.GetRequiredService<SomeService>()
填充null
元素。請參閱此處的源代碼。 如果服務實例仍然為null
,則使用default
值。例如,讓我們說:
public CustomMiddleware(RequestDelegate next, A a, B b, C c, D d, E e){ ... }
然后我們在注冊中間件時傳入兩個參數:
app.UseMiddleware(c, a)
其中c
是C
Type 的實例, a
是A
Type 的實例。 所以給定的givenParameters
數組是[next,c, a]
要創建CustomMiddleware
的實例,編譯器需要知道完整的構造函數參數值。 DI 擴展在兩個階段內獲取此構造函數參數值數組 ( _parameterValues
)。參見:
stage2 的工作方式如下:
b'= sp.GetService(B);
if b' == null :
b' = default value of B
正如您在上面看到的, ActivatorUtilities.CreateInstance(sp,mw,args)
API 自動處理順序和丟失的參數。
附帶說明一下,約定俗成的中間件在啟動時激活,並且始終是單例。 如果您想使用范圍服務,請參閱此線程
但我正在尋找一些保證,即我所做的一切都沒有問題。
嗯,這是基於意見的。 雖然在你的情況下一切正常,但我相信沒關系。 但我更喜歡使用ASP.NET Core
引入的選項模式。
public class CustomMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<CustomMiddleware> _logger;
private readonly CustomMiddlewareOptions _options;
public CustomMiddleware(RequestDelegate next, ILogger<CustomMiddleware> logger, IOptions<CustomMiddlewareOptions> options)
{
_next = next;
_logger = logger;
_options = options.Value;
}
//...
}
為此,您需要在Startup
配置CustomMiddlewareOptions
services.Configure<CustomMiddlewareOptions>(options =>
{
options.Id = 1;
options.Name = "options";
//set other properties
});
在這種情況下,您應該添加不帶參數的中間件
app.UseMiddleware<CustomMiddleware>();
筆記
也值得一看RequestLocalizationMiddleware
public RequestLocalizationMiddleware(RequestDelegate next, IOptions<RequestLocalizationOptions> options)
以及框架如何讓您在ApplicationBuilderExtensions 中使用中間件
public static IApplicationBuilder UseRequestLocalization(this IApplicationBuilder app)
{
//omitted
return app.UseMiddleware<RequestLocalizationMiddleware>();
}
或者
public static IApplicationBuilder UseRequestLocalization(this IApplicationBuilder app, RequestLocalizationOptions options)
{
//omitted
return app.UseMiddleware<RequestLocalizationMiddleware>(Options.Create(options));
}
正如您所看到的, ASP.NET Core
開發人員也更喜歡在中間件構造函數中使用IOptions
,並且在手動指定附加參數時,他們只是將它們包裝到Options
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.