Why not simply have a single extension method on the IApplicationBuilder
that contains the whole implementation?
Basically that is what the first Extension method does, you are forced in that case to pass through the WelcomePageOptions
which contains the whole implementation or at least all the optional arguments.
public static IApplicationBuilder UseWelcomePage(this IApplicationBuilder app, WelcomePageOptions options)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
return app.UseMiddleware<WelcomePageMiddleware>(Options.Create(options));
}
Ultimately it calls the OWIN UseMiddleware
registration method which requires you to know the specific implementation syntax and the required types because UseMiddleware
accepts an untyped and unspecified number of arguments via the params object[] args
parameter. You can call this OWIN registration yourself but without type validation your run the risk of runtime errors when you pass in incompatible options.
The extension method is a helper that validates and enforces the specific input types and improves the natural readability of the code. That alone is reason enough to use the extension methods but this makes it discoverable via intellisense which helps us to rapidly consume middleware without needing to know what all of the possible options might be.
Adding Middleware as Extension method to IAppBuilder
Understanding and Creating OWIN Middlewares - Part 1
This older documentation on how to author Middleware outlines the different styles of implementation, what you see today is a simplification of all of those options presented as a Strongly Typed Extension to IAppBuilder.
The other 3 extensions in WelcomePageExtensions.cs offer common implementations to reduce the boiler-plate code that you might write and ultimately get wrong.
Every line of code I write to consume someone else's assemblies has the potential to fail, but it will be MY fault. A good framework author will provide different implementation options that suit standard patterns of compatibility so that callers are less likely to make silly mistakes.
UseWelcomePage(this IApplicationBuilder app, PathString path)
PathString
is an AspNetCore concept that encapsulates the management of escaping, processing or constructing URI strings, for a pure ASP.Net Core codebase, this is the standard way to define an unescaped route to a resource.
UseWelcomePage(this IApplicationBuilder app, string path)
The string typed path is provided for lazier implementations (IMO) or codebases that have been migrated from ASP.Net. These two overloads cover most use cases for the WelcomePage.
The last Extension in that class is a simple overload that allows you to omit the configuration options altogether.
UseWelcomePage(this IApplicationBuilder app)
This style of overloading the extension method, instead of making the configuration parameter optional is preferred in this case because there are already multiple overloads with the same name. Mixing overloads and optional parameters can make the resolution of which method to execute ambiguous, so as a design pattern, use one technique, not both. This comment thread is an example of the discussions that go on if you get the balance wrong: https://thedailywtf.com/articles/comments/extending-yourself
Extensions methods are used in this way throughout the framework, not just in middleware, it allows the author of the intended functionality to write a single method with the optimal parameters to get the job done, or as in the case with middleware, we are forced to expose untyped or generic prototypes conforming to specific interfaces which have ambiguous usage.
The wrapper extensions are then added to make it easier to discover the functionality or to use it in different contexts without forcing the end user to cast their current variables into what might seem exotic types which might then be passed through to untyped (object
) parameters.
This pattern also emerges when new versions of the framework improve the core functionality but they want to maintain backward compatibility with exiting type runtimes.
For more background on the extension method pattern in general have a read over Extension Methods Best Practises on the VB devblog.