使用 IMiddleware 时添加自定义中间件不起作用
Posted
技术标签:
【中文标题】使用 IMiddleware 时添加自定义中间件不起作用【英文标题】:Adding custom middleware not working when using IMiddleware 【发布时间】:2020-04-29 23:25:07 【问题描述】:我正在尝试将自定义中间件添加到管道中(为方便起见,我将选择 .NET Core 文档示例)。 假设我们希望在调用 API 时设置西班牙文化。 这是完美运行的代码:
public class RequestCultureMiddleware
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
_next = next;
public async Task InvokeAsync(HttpContext context)
CultureInfo.CurrentCulture = new CultureInfo("es-ES");
CultureInfo.CurrentUICulture = new CultureInfo("es-ES");
// Call the next delegate/middleware in the pipeline
await _next(context);
public static class RequestCultureMiddlewareExtensions
public static IApplicationBuilder UseRequestCulture(
this IApplicationBuilder builder)
return builder.UseMiddleware<RequestCultureMiddleware>();
还有 Startup 类:
public class Startup
public Startup(IConfiguration configuration)
Configuration = configuration;
public IConfiguration Configuration get;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
services.AddControllers();
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
//here is our custom middleware!
app.UseRequestCulture();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
endpoints.MapControllers();
);
这很好,但正如您所见,RequestCultureMiddleware 没有实现接口或基类/抽象类。您只需要记住在定义中间件以创建接收下一个中间件的构造函数时,还需要创建一个专门称为“InvokeAsync”的方法,并以“HttpContext”作为参数。
我试图找到一个合同......一个基类或一个接口,猜猜是什么,我们有“IMiddleware”,它是“Microsoft.AspNetCore.Http”程序集的一部分。哇,太完美了。让我们实现它。
界面如下:
namespace Microsoft.AspNetCore.Http
//
// Summary:
// Defines middleware that can be added to the application's request pipeline.
public interface IMiddleware
//
// Summary:
// Request handling method.
//
// Parameters:
// context:
// The Microsoft.AspNetCore.Http.HttpContext for the current request.
//
// next:
// The delegate representing the remaining middleware in the request pipeline.
//
// Returns:
// A System.Threading.Tasks.Task that represents the execution of this middleware.
Task InvokeAsync(HttpContext context, RequestDelegate next);
这是实现:
public class RequestCultureMiddleware : IMiddleware
public Task InvokeAsync(HttpContext context, RequestDelegate next)
CultureInfo.CurrentCulture = new CultureInfo("es-ES");
CultureInfo.CurrentUICulture = new CultureInfo("es-ES");
// Call the next delegate/middleware in the pipeline
return next(context);
public static class RequestCultureMiddlewareExtensions
public static IApplicationBuilder UseRequestCulture(
this IApplicationBuilder builder)
return builder.UseMiddleware<RequestCultureMiddleware>();
但是,在运行 API 时,我在运行时收到以下错误:
System.InvalidOperationException: No service for type 'WebApplication1.RequestCultureMiddleware' has been registered.
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.AspNetCore.Http.MiddlewareFactory.Create(Type middlewareType)
at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass5_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
如果不使用扩展名“UseMiddleware”,我应该如何注册这个中间件? 谢谢。
【问题讨论】:
【参考方案1】:我确信这个问题在 5 个月后很久以前就已经解决了,但我写这个建议是为了以防万一。
问题是自定义中间件程序的“InvokeAsync”方法没有被执行,即使你在启动的“配置”方法中内置了它。
前几天我遇到了同样的问题并解决了,但我在 app.UseEndpoints 方法之前放入了内置代码。
在你的情况下
app.UseAuthorization();
app.UseRequestCulture(); // <- this way.
app.UseEndpoints(endpoints =>
endpoints.MapControllers();
);
顺便说一句,如果你把它放在app.UseEndpoints方法之后,构造函数会被调用,但是InvokeAsync方法不会被执行。
【讨论】:
这完全符合我的解释。读者应该遵循这两个答案 这对我有用。我没有实现任何接口。我只是将我的中间件移到了UseEndpoints
上面,它就开始工作了。【参考方案2】:
您正在使用factory-based middleware。如这些文档中所述,您错过了一个重要步骤:
...容器中注册的IMiddlewareFactory实例用于解析IMiddleware实现,而不是使用基于约定的中间件激活逻辑。中间件在应用的服务容器中注册为scoped or transient service。
在您的情况下,注册看起来像这样:
public void ConfigureServices(IServiceCollection services)
// ...
services.AddTransient<RequestCultureMiddleware>();
【讨论】:
以上是关于使用 IMiddleware 时添加自定义中间件不起作用的主要内容,如果未能解决你的问题,请参考以下文章
将自定义中间件添加到 Laravel Passport 端点
Download Middlewares的作用?如何自定义ItemPipeline,写出需要实现的