Owin - Katana core middleware patterns

Introduction

Katana supports 4 different convention based patterns for adding middlewares in its lowest level. They are,

  1. Delegate Pattern
  2. Instance Pattern
  3. Generator/Nested Delegate Pattern
  4. Constructor Type or Type Pattern

All the other formats/patterns supported through IAppBuilder’s extension methods are implemented using one of these 4 patterns under the hood.

I couldn’t find any examples in the web which show how to use these patterns, so I am creating it for my future reference.

Delegate Pattern

Delegate pattern accepts the following delegate as a first parameter for the Use method of IAppBuilder and any number of additional parameter of any type

// Delegate Pattern
Func<AppFunc, AppFunc>

where AppFunc is Func<IDictionary<string, object>, Task>>

The delegate takes AppFunc as parameter and returns AppFunc. The parameter represents the next middleware in the pipeline and return value represents the implementation of the middleware we are adding.

// Delegate Pattern Implementation
public void Configuration(IAppBuilder builder)
{
    builder.Use(new Func<AppFunc, AppFunc>(next => async env =>
    {
        Console.WriteLine("From Delegate Middleware - Start");
        await next(env);
        Console.WriteLine("From Delegate Middleware - End");

    }));
}

In the example above, the middleware we are adding prints something in the console, calls next middleware in the pipeline and once it is complete, it continues by executing rest of the code in the middleware we are adding.

The following example shows the delegate pattern with additional parameters

// Delegate Pattern with additional parameters
public void Configuration(IAppBuilder builder)
{
   builder.Use(new Func<AppFunc, string, AppFunc>((next, param) => async (env) =>
    {
        Console.WriteLine("From Delegate Middleware with param- Start");
        Console.WriteLine("Paramater value : {0}", param);
        await next(env);
        Console.WriteLine("From Delegate Middleware with param- End");

    }), "additional param");
}

Instance Pattern

To use this pattern to add middlewares, we need newable types with the following two methods

// Instance Pattern method signatures
public void Initialize(AppFunc next, params object[] args);
public Task Invoke(IDictionary<string, object> environment);

Here is an example of middleware created using instance pattern

// Instance Pattern with parameter
public void Configuration(IAppBuilder builder)
{
 	builder.Use(new InstanceMiddlewareWithParam(), "instance param value");
}

public class InstanceMiddlewareWithParam
{
    private AppFunc _next;
    private string _param;
    public void Initialize(AppFunc next, string param)
    {
        _next = next;
        _param = param;
    }

    public async Task Invoke(IDictionary<string, object> environment)
    {
        Console.WriteLine("From Instance Middleware with param - Start");
        Console.WriteLine("Parameter value: {0}", _param);
        await _next(environment);
        Console.WriteLine("From Instance Middleware with param - Start");
    }

}

Generator/Nested Delegate Pattern

To use this pattern to create middlewares, we need a newable type with the following method

// Generator / Nested Delegate pattern
public AppFunc Invoke(AppFunc next, params object[] args);

This method takes next middleware in the pipeline and any number of additional arguments and returns the implementation of current middleware. Here is an example

// Middleware using Generator / Nested Delegate pattern

public void Configuration(IAppBuilder builder)
{
 builder.Use(new GeneratorMiddlewareWithParam(), "generator param value");
}

public class InstanceMiddlewareWithParam
{
    private AppFunc _next;
    private string _param;
    public void Initialize(AppFunc next, string param)
    {
        _next = next;
        _param = param;
    }

    public async Task Invoke(IDictionary<string, object> environment)
    {
        Console.WriteLine("From Instance Middleware with param - Start");
        Console.WriteLine("Parameter value: {0}", _param);
        await _next(environment);
        Console.WriteLine("From Instance Middleware with param - Start");
    }

}

Constructor Type / Type Pattern

A type with the following constructor and method can be added a middleware using this pattern

 // Constructor Type / Type pattern
 public Ctor(AppFunc next, params object[] args);
 public Task Invoke(IDictionary<string, object> env);

Here is an example

// Middleware using Constructor Type / Type pattern

public void Configuration(IAppBuilder builder)
{
     builder.Use(typeof(ConstructorTypeMiddlewareWithParam), "type param value");
}

public class ConstructorTypeMiddlewareWithParam
{
    private readonly AppFunc _next;
    private readonly string _param;

    public ConstructorTypeMiddlewareWithParam(AppFunc next, string param)
    {
        _next = next;
        _param = param;
    }

    public async Task Invoke(IDictionary<string, object> env)
    {
        Console.WriteLine("From Constructor Type Middleware - Start");
        Console.WriteLine("Parameter value: {0}", _param);
        await _next(env);
        Console.WriteLine("From Constructor Type Middleware - End");
    }
}
comments powered by Disqus