Last weekend was another long weekend in Canada - at least in the part of Canada where I live, British Columbia, so I had some time to return to working on my bog application - Naif.Blog. The final piece of the puzzle - how can we make our simple blog application support multiple different blogs from the same code-base. This feature can save hosting dollars as it allows a single hosted site to support many different blogs, and is usually termed - multi-tenancy.
As usual there are many ways to skin a cat, but the approach I decided to take was to use Middleware to determine which blog is making the request.
In order to determine which blog context we are executing in, I have created an interface IApplicationContext and a matching object ApplicationContext. We will use this object to manage our context as a service using Dependency Injection. ASP.NET Core’s DI component supports a number of lifecycle types, and one of these is Scoped which creates a new instance for each request, so in Startup.cs we add the single statement
Now at the beginning of the request a new instance of ApplicationContext will be created and any object that needs the context can retrieve it from the DI container.
The application context is a simple object, as shown below.
It has three properties - an Id, a collection of all the blogs and the current blog. The Blog model is not really a new class. In previous iterations of Naif.Blog there was a BlogOptions class and this class has been renamed Blog and extended with a couple of additional properties.
The BlogOptions object was loaded once at start up (from the appsettings.json file) as these options were static. But now we need to load the relevant Blog detail based on the hostname in the request, and this is where ASP.NET Core Middleware comes in.
ASP.NET Core Middleware
The ASP.NET Core pipeline is very flexible and allows developers to add components to the pipeline, as Middleware. We have already used middleware components without realizing what they are in Startup.cs. For example app.UseStaticFiles() invokes the static files middleware and app.UseMvc invokes the MVC middleware.
ASP.NET Core Middleware is a simple class with a single method - Invoke. The constructor takes a RequestDelegate which represents the next middleware component to invoke, and the Invoke method takes an HttpContext object as its first parameter - both methods support the injection of additional objects through Dependency Injection.
The constructor for our ApplicationContextMiddleware class is as follows
and the Invoke method is
The Invoke method first retrieves all the blogs from the blogs.json file. It then retrieves the blog whose Url property matches the host url for the request, and sets the CurrentBlog property of the ApplicationContext object. Finally the next Middleware component in the pipeline is called.
Our custom middleware can now be added to the request pipeline by adding a call to the UseMiddleware method of the IApplicationBuilder interface. I have implemented this using an extension method:
which can then be invoked in the Startup.cs class.
Now our Middleware will execute on each request and build the ApplicationContext object. We can then use that object by injecting it into our Controllers, ViewComponents and Views just as we would any other service.
The source for Naif.Blog can be found at https://github.com/cnurse/Naif.Blog. If you want to find the state of the repository used in this post then you can find that at the tagged release v0.0.5 (https://github.com/cnurse/Naif.Blog/releases/tag/v0.0.5)