skip to content
Stylized letter A in teal with pink sparkle accents Khalid Abuhakmeh

MiddlewareBuilder For ASP.NET Core 1.0 RC2

/ 3 min read

Found this post helpful?

Buy me a coffee
Table of Contents

I was reading Bill Boga’s ( @Bogaiv ) blog post “Environment name in ASP.NET Core 1.0 RC2” and noticed that the Configure method at the bottom of his post looked very iffy through no fault of his own.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
// do stuff on your local machine
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
if (env.IsStaging())
{
// do stuff on your pre-prod. machine
}
if (env.IsProduction())
{
// do stuff on your prod. machine
}
if (env.IsEnvironment("QA"))
{
// do stuff on your QA machine
}
}

If you create a new ASP.NET Core 1.0 RC2 application using Visual Studio, the Configure method looks like this.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentity();
// Add external authentication middleware below. To configure them please see http://go.microsoft.com/fwlink/?LinkID=532715
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}

Take note of the if/else block. I wanted to see if I could write a more fluent interface that accomplishes the same thing. This is my recreation of the default Configure method.

Another Approach

new MiddlewareBuilder(app, env)
.Development((a, e) =>
{
a.UseDeveloperExceptionPage()
.UseDatabaseErrorPage()
.UseBrowserLink();
})
.When(!env.IsDevelopment(), (a, e) =>
{
a.UseExceptionHandler("/Home/Error");
})
.Always((a, e) =>
{
a.UseStaticFiles()
.UseIdentity()
.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
})
.Build();

I’m not sure I like my approach over the original if/else approach so I’d like your feedback. You can find the code below if you want to try out the MiddlewareBuilder.

Some caveats to keep in mind:

  • The order of registration is determined inside the MiddlewareBuilder
  • MiddlewareBuilder only supports one action per environment
  • tested very minimally
  • registration only occurs once Build is called

The Code

public class MiddlewareBuilder
{
const string AlwaysKey = "__always";
const string DevelopmentKey = "__development";
const string ProductionKey = "__production";
const string StagingKey = "__staging";
private readonly string[] allKeys = {AlwaysKey, DevelopmentKey, StagingKey, ProductionKey};
private Dictionary<string, Action<IHostingEnvironment, IApplicationBuilder>>
Environments { get; }
= new Dictionary<string, Action<IHostingEnvironment, IApplicationBuilder>>(StringComparer.CurrentCultureIgnoreCase);
private readonly IApplicationBuilder app;
private readonly IHostingEnvironment env;
public MiddlewareBuilder(IApplicationBuilder app, IHostingEnvironment env)
{
if (env == null) throw new ArgumentNullException(nameof(env));
if (app == null) throw new ArgumentNullException(nameof(app));
this.env = env;
this.app = app;
}
public MiddlewareBuilder Always(Action<IHostingEnvironment, IApplicationBuilder> setup)
{
Environments.Add(AlwaysKey, setup);
return this;
}
public MiddlewareBuilder Development(Action<IHostingEnvironment, IApplicationBuilder> setup)
{
Environments.Add(DevelopmentKey, setup);
return this;
}
public MiddlewareBuilder Production(Action<IHostingEnvironment, IApplicationBuilder> setup)
{
Environments.Add(ProductionKey, setup);
return this;
}
public MiddlewareBuilder Staging(Action<IHostingEnvironment, IApplicationBuilder> setup)
{
Environments.Add(StagingKey, setup);
return this;
}
public void Build()
{
// always
Action<IHostingEnvironment, IApplicationBuilder> always;
Environments.TryGetValue(AlwaysKey, out always);
always?.Invoke(env, app);
//development
if (env.IsDevelopment())
{
Action<IHostingEnvironment, IApplicationBuilder> development;
Environments.TryGetValue(DevelopmentKey, out development);
development?.Invoke(env, app);
}
// staging
if (env.IsStaging())
{
Action<IHostingEnvironment, IApplicationBuilder> staging;
Environments.TryGetValue(StagingKey, out staging);
staging?.Invoke(env, app);
}
// production
if (env.IsProduction())
{
Action<IHostingEnvironment, IApplicationBuilder> production;
Environments.TryGetValue(StagingKey, out production);
production?.Invoke(env, app);
}
// remaining
var remaining = Environments
.Where(x => !allKeys.Contains(x.Key, StringComparer.CurrentCultureIgnoreCase))
.Where(x => env.IsEnvironment(x.Key))
.Select(x => x.Value);
foreach (var action in remaining)
{
action(env, app);
}
}
}

Related Posts