7

I want to know how can I have middleware architecture like asp.net core in my application ?

Which pattern needs for this goal ?

Is there any reference for designing like this for adding new features and ... ?

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?}");
    });
}

With very simple Configure method we can add new features to application How they implement something like this ?

JKL
  • 471
  • 1
  • 5
  • 6
  • What are you adding in specific or you want to know how to do it generically? – Nico Aug 17 '16 at 04:46
  • @Nico I dont have any idea about that, I dont know this a pattern or a combination of other patterns at first I want to have some information about that or simple sample – JKL Aug 17 '16 at 04:49

2 Answers2

16

I've made a simple prototype implementation for a project I'm working on that has nothing to do with ASP.NET Core so people that don't understand its architecture or its implementation might have easier time grasp the concept.

The design pattern at play here is called the Chain of Responsibility pattern.

So the first thing we need is to define the delegate for the application:

public delegate void InputDelegate(char key);

Then we need something to consume it so here is a very simple event/main loop implementation:

internal class TerminalHost
{
    private InputDelegate _inputDelegate;

    public TerminalHost()
    {
        // Initializes the first delegate to be invoked in the chain.
        _inputDelegate = Console.Write;
    }

    internal void Start()
    {
        CancellationTokenSource tokenSource = new CancellationTokenSource();

        while (!tokenSource.IsCancellationRequested) {
            ConsoleKeyInfo keyInfo = Console.ReadKey();

            _inputDelegate(keyInfo.KeyChar);
        }
    }

    /// <summary>
    /// Adds the middleware to the invocation chain. 
    /// </summary>
    /// <param name="middleware"> The middleware to be invoked. </param>
    /// <remarks>
    /// The middleware function takes an instance of delegate that was previously invoked as an input and returns the currently invoked delegate instance as an output.
    /// </remarks>
    internal void Use(Func<InputDelegate, InputDelegate> middleware)
    {
        // Keeps a reference to the currently invoked delegate instance.
        _inputDelegate = middleware(_inputDelegate);
    }
}

And finally, we need to create an instance of the TerminalHost class and call the Use method so this would be something like this:

class Program
{
    static void Main(string[] args)
    {
        TerminalHost terminal = new TerminalHost();

        terminal.Use(next => ch => {
            Console.WriteLine(ch);

            next(ch);
        });

        terminal.Start();
    }
}

I hope it make sense and that it's helpful to someone out there! :)

t3chb0t
  • 16,340
  • 13
  • 78
  • 118
iam3yal
  • 2,188
  • 4
  • 35
  • 44
  • Hello, just wondering if that is not an issue that each middleware is executed in opossite order than anyone would expect? I Guess in pure chain of responsibility pattern it might not make any problems. But it just confused me a bit ;) – jgasiorowski Sep 29 '22 at 22:23
  • @jgasiorowski The order in which things gets executed is really irrelevant to the pattern, I've demonstrated the concept, the implementation is meaningless and depends on the context, I needed something like this and the example was based on my needs for a project I was working on, there are other ways to implement it with different requirements. – iam3yal Oct 01 '22 at 04:51
13

The first thing I would do is read Middleware - Asp.Net Documentation

This will help understand how middleware is used and the pracitices to follow to implement your own custom middleware.

taken directly from docs

Middleware are software components that are assembled into an application pipeline to handle requests and responses. Each component chooses whether to pass the request on to the next component in the pipeline, and can perform certain actions before and after the next component is invoked in the pipeline. Request delegates are used to build the request pipeline. The request delegates handle each HTTP request.

Now as this states it allows the components to handle the requests and responses. The middle ware is executed in the order it is added to the application middleware collection.

app.UseStaticFiles(); adds the middlware to handle static files (images, css, scripts etc).

Now say you wish to add your own handling in the pipeline you can create a middleware class as a sample below.

public class GenericMiddleware
{
    public GenericMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    readonly RequestDelegate _next;

    public async Task Invoke(HttpContext context, IHostingEnvironment hostingEnviroment)
    {
        //do something

        await _next.Invoke(context);
    }
}

There are a few interesting points here. First off the constructor takes a RequestDelegate which is essentially (but not exactly) the next middleware object that will be invoked.

Next we implement our own Invoke method. This method should take the HttpContext as a paramater and will also allow for additional dependencies to be injected from the IoC container (in my example the IHostingEnvironment instance).

This method then executes the code it needs to execute and then calls await _next.Invoke(context); which executes the RequestDelegate to call the next middleware class.

Now to add this to the application middle ware you can simply call

app.UseMiddleware<GenericMiddleware>(); in your Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) method in the startup.cs file.

Or create a simple extension method as.

public static class GenericMiddlewareExtensions
{
    public static IApplicationBuilder UseGenericMiddleware(this IApplicationBuilder app)
    {
        app.UseMiddleware<GenericMiddleware>();
        return app;
    }
}

Which is then called app.UseGenericMiddleware();

Now how and why you implement your own middleware is up to you. However as stated it is up to the middleware to call the next RequestDelegate if it doesnt then it stops the request.

Here is an example in one of my projects that checks to see if the database is installed. If it isnt redirects the request back to the installation page.

public class InstallationMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    public InstallationMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
    {
        _next = next;
        _logger = loggerFactory.CreateLogger<InstallationMiddleware>();
    }

    public async Task Invoke(HttpContext context)
    {
        _logger.LogInformation("Handling request: " + context.Request.Path);
        if (context != null && !DatabaseHelper.IsDatabaseInstalled())
        {
            if (!context.Request.Path.ToString().ToLowerInvariant().StartsWith("/installation"))
            {
                context.Response.Redirect("/installation");
                return;
            }
        }

        await _next.Invoke(context);
        _logger.LogInformation("Finished handling request.");
    }
}

As you can see if !DatabaseHelper.IsDatabaseInstalled() we redirect the response but dont invoke the _next RequestDelegate.

Again the documentation speaks for itself.

Nico
  • 12,493
  • 5
  • 42
  • 62
  • Thanks you define how that is works but Is there any implementation of this architecture as sample ? for example when we say builder pattern this have many sample about implementation so I need a sample in general purpose not for Http only ? Is this pattern an alternative of MEF ? Why Microsoft did not use mef for that (adding features and ...) ? – JKL Aug 17 '16 at 05:08
  • @JKL The middleware (for asp.net core) is directly related to the Http Context only. Maybe I am not following exactly what you are looking for. – Nico Aug 17 '16 at 05:15
  • What is your suggestion for adding removing features to application when needed in .NET Core ? when someone needs adding himself/herself features for application ? in .NET Framework I used MEF + Appdomain but both of them does not exist in .NET Core. – JKL Aug 17 '16 at 05:19
  • If you mean like adding additional modules and features we have had to implement our own system. We created the ability to (via configuration) install modules into the application. This was not an easy feat and have a post that talks about adding in controllers from another assembly http://stackoverflow.com/questions/37608298/mvc-6-rc2-controllers-in-another-assembly/37647605#37647605 – Nico Aug 17 '16 at 05:23