问题描述
当我们在ASP.NET Core中定义和使用中间件(Middleware)的时候,有什么好的办法可以给中间件传参数吗?
解决方案
在ASP.NET Core项目中添加一个POCO类来传递参数到中间件,例如下面的GreetingOptions类
public class GreetingOptions { public string GreetAt { get; set; } public string GreetTo { get; set; } }
然后添加一个中间件GreetingMiddleware
public class GreetingMiddleware { private readonly RequestDelegate next; private readonly GreetingOptions options; public GreetingMiddleware( RequestDelegate next, GreetingOptions options) { this.next = next; this.options = options; } public async Task Invoke( HttpContext context) { var message = $"Good {this.options.GreetAt} {this.options.GreetTo}"; await context.Response.WriteAsync(message); } }
通过添加一个扩展方法类GreetingMiddlewareExtension,来配置中间件GreetingMiddleware,并传递参数GreetingOptions options
public static class GreetingMiddlewareExtension { public static IApplicationBuilder UseGreeting(this IApplicationBuilder app, GreetingOptions options) { return app.UseMiddleware<GreetingMiddleware>(options); } }
在ASP.NET Core项目中Startup类的Configure方法中,启用GreetingMiddleware中间件,并传递参数到中间件
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseGreeting(new GreetingOptions { GreetAt = "Morning", GreetTo = "Tahir" }); }
通过添加一个扩展方法类GreetingMiddlewareExtension,来配置中间件GreetingMiddleware,并通过委托Action<GreetingOptions> configureOptions来传递参数GreetingOptions
public static class GreetingMiddlewareExtension { public static IApplicationBuilder UseGreeting(this IApplicationBuilder app, Action<GreetingOptions> configureOptions) { var options = new GreetingOptions(); configureOptions(options); return app.UseMiddleware<GreetingMiddleware>(options); } }
在ASP.NET Core项目中Startup类的Configure方法中,启用GreetingMiddleware中间件,并通过委托Action<GreetingOptions>传递参数到中间件
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseGreeting(options => { options.GreetAt = "Morning"; options.GreetTo = "Tahir"; }); }
讨论
在前面的一篇文章中我们讨论了,定义和使用中间件(Middleware)的最好方法是,将中间件定义在一个单独的类中,然后通过扩展方法将其添加到ASP.NET Core的中间件管道(pipeline)中。
我们可以通过上面介绍的解决方案A和B,将需要传递到中间件的参数都封装为一个POCO类,并创建一个扩展方法,用下面两种方式作为扩展方法的参数:
注意:POCO实例是通过. UseMiddleware()方法的params object[]参数来传递到中间件的构造函数中的,也就是说我们可以通过params object[]来传递任意多个参数到中间件中,只要类型和中间件的构造函数参数匹配即可。我们还可以将依赖注入参数和POCO实例参数一起定义在中间件的构造函数中,如下所示:
public class GreetingMiddleware { private readonly RequestDelegate next; private readonly GreetingOptions options; //IHostingEnvironment env是依赖注入参数,GreetingOptions options是POCO实例参数 public GreetingMiddleware(RequestDelegate next, IHostingEnvironment env, GreetingOptions options) { string environmentName = env.EnvironmentName; this.next = next; this.options = options; } public async Task Invoke( HttpContext context) { var message = $"Good {this.options.GreetAt} {this.options.GreetTo}"; await context.Response.WriteAsync(message); } }
配置依赖注入服务
前面介绍的两种解决方案同样也可以用来配置依赖注入(dependency injection)服务的容器。
为了演示,我们先添加一个POCO类MessageOptions和用到的枚举MessageFormat
public enum MessageFormat { None, Upper, Lower } public class MessageOptions { public MessageFormat Format { get; set; } }
然后添加一个依赖注入接口IMessageService,及其实现类MessageService
public interface IMessageService { string FormatMessage(string message); }
public class MessageService : IMessageService { private readonly MessageOptions options; public MessageService(MessageOptions options) { this.options = options; } public string FormatMessage(string message) { // use options return this.options.Format == MessageFormat.None ? message : this.options.Format == MessageFormat.Upper ? message.ToUpper() : message.ToLower(); } }
同样,接下来我们就用两种解决方案,来定义MessageService的扩展方法类MessageServiceExtension
解决方案A:实例类型(Instance Type)
public static class MessageServiceExtension { // Instance Type public static IServiceCollection AddMessageFormatter(this IServiceCollection services, MessageOptions options) { return services.AddScoped<IMessageService>(factory => { return new MessageService(options); }); } }
在ASP.NET Core项目中Startup类的ConfigureServices方法中,注册IMessageService和MessageService的依赖注入服务
// Instance Type public void ConfigureServices(IServiceCollection services) { services.AddMessageFormatter(new MessageOptions { Format = MessageFormat.Lower }); }
解决方案B:方法类型(Function Type)
public static class MessageServiceExtension { // Function Type public static IServiceCollection AddMessageFormatter(this IServiceCollection services, Action<MessageOptions> configureOptions) { var options = new MessageOptions(); configureOptions(options); return services.AddScoped<IMessageService>(factory => { return new MessageService(options); }); } }
在ASP.NET Core项目中Startup类的ConfigureServices方法中,注册IMessageService和MessageService的依赖注入服务
// Function Type public void ConfigureServices(IServiceCollection services) { services.AddMessageFormatter(options => { options.Format = MessageFormat.Lower; }); }