Click here to Skip to main content
15,867,330 members
Articles / Web Development / ASP.NET / ASP.NET Core

Deep Dive into OWIN Katana

Rate me:
Please Sign up or sign in to vote.
5.00/5 (30 votes)
17 Sep 2020CPOL42 min read 46K   384   45   11
Web specification and framework
This article mostly focuses on OWIN and Katana which is an implementation of OWIN. The inner working of OWIN Katana is explained in detail. A short chapter is allocated to discuss the Web API development with OWIN Katana, followed by a brief introduction to the ASP.NET Core at the end.

[Note: You can use the downloaded projects as a template to start OWIN Katana or WebAPI applications.]

Topic Outline

Introduction

I was asked to give a presentation about OWIN and Web API to a different development team, as they wanted an introduction to the technology and no one in the team had exposure to that technology before. Prior to that, I have developed a number of internal API for our company. With the presentation slides and materials, I decided to write them down as a blog post as well, so it will be documented properly. I put a lot of extra information compared to the original presentation to cover a broader audience in the internet.

Even with recent revolutionary changes in the ASP.NET Core, I found out that understanding OWIN is fundamental to understanding the framework and features of the ASP.NET Core. There are many concepts, and architectural designs in OWIN which still apply to ASP.NET Core. This post mostly focuses on OWIN and Katana which is an implementation of OWIN. The inner working of OWIN Katana is explained in detail. After that, a short chapter is allocated to discuss the Web API development with OWIN Katana, followed by a brief introduction to the ASP.NET Core at the end.

Part 1 - What is OWIN

OWIN stands for Open Web Interface for .NET. OWIN is a open standard specification which defines a standard interface between .NET web servers and web applications. The aim is to provide a standard interface which is simple, pluggable and lightweight.

OWIN is motivated by the development of web frameworks in other coding languages such Node.js for JavaScript, Rack for Ruby, and WSGI for Phyton. All these web frameworks are designed to be fast, simple and they enable the development of web applications in a modular way. In contrast, prior to OWIN, every .NET web application required a dependency on >System.Web.dll, which tightly coupled with Microsoft's IIS (Internet Information Services). This meant that .NET web applications came with a number of application component stacks from IIS, whether they were actually required or not. This made .NET web applications, as a whole, heavier and they performed slower than their counterparts in other coding languages in many benchmarks

OWIN was initiated by members of Microsoft's communities; such as C#, F# and dynamic programming communities. Thus, the specification is largely influenced by the programming paradigm of those communities. From initial prototyping, OWIN was then reduced to just a common specification. It basically specifies, as shown in Figure 1:

  1. Type of object to be passed around, between web servers and web applications, and between web application components. This is also called the environment object.
  2. The structure of web application components. The structure should be simple and is represented in a consistent way.

Other constraints include having no dependency, except for Foundation Class Language (FCL) types and the need to support async processing.

Image 1

Figure 1. Scope of OWIN

Image 2

Figure 2. OWIN Specifications (Interaction between web server and web application)

Figure 2 shows OWIN overview of web servers and web applications interaction, which contains at least 4 important aspects:

  1. OWIN introduces a key value pair dictionary, Dictionary<string, object>, as the environment object to be passed between web servers. and web applications, and in-between web application components. OWIN also specifies the following keys:
    Required Key Type (Default)
    yes owin.RequestBody Stream
    yes owin.RequestHeaders IDictionary<string, string[]>
    yes owin.RequestMethod string
    yes owin.RequestPath string
    yes owin.RequestPathBase string
    yes owin.RequestProtocol string
    yes owin.RequestQueryString string
    yes owin.RequestScheme string
    yes owin.ResponseBody Stream
    yes owin.ResponseHeaders IDictionary<string, string[]>
    no owin.ResponseStatusCode int(200)
    no owin.ResponseReasonPhrase string (OK)
    no owin.ResponseProtocol string (HTTP/1.1)
    yes owin.CallCancelled CancellationToken
    yes owin.Version string
    (Source: http://owin.org)

    All these key values are not nullable, and should only be compared by their ordinal code (case sensitive). Web servers are responsible for constructing this environment object on receipt of a http request, and pass it to a web application.

  2. Each web application component is modelled as a delegate function, Func<IDictionary<string, object>,Task> which means the component accepts an environment object IDictionary<string, object>, and returns an awaitable Task. This also means that the delegate supports asynchronous calls. The delegate function is given a name AppFunc, and web application components in OWIN are called middleware.
  3. A pipeline model is used to structure middleware. Unfortunately, OWIN 1.0 does not specify how middleware should be chained into a pipeline. A later draft attempted, targeting OWIN 1.0, to include specification for middleware and pipeline builder. It is now an expired working in progress draft since 1 March 2016 and seems to be abandon. Even though it is not specified, there will be always a need for a pipeline/middleware/application builder in a real implementation.

    So what is the requirement for the builder to be able to register middleware? The pipeline structure implicates that the implementation of a middleware can optionally call other or 'next' middleware. For example, the first middleware calls the next middleware and so on, until the last one which terminates or returns the call directly (The first middleware is normally called a pass-through middleware, and the last one is named a terminating middleware). In order to be able to chain middleware into a pipeline as a 'delegate' function (delegate is a first class citizen in OWIN, mostly influenced by functional programming paradigm), the builder needs a delegate with the signature of Func<AppFunc, AppFunc>. The first, or the argument, AppFunc is the placeholder for the 'next' middleware, while the second one, or the return, is the middleware body. The middleware body calls the 'next' middleware if it is a pass-through middleware.

    When the builder builds the pipeline, it executes the registered delegate functions in a reversed order, one after another and passes the result of the last execution (a middleware body) as the argument of the next execution ('next' argument of the executed delegate function), until the first middleware body (AppFunc) is retrieved and acts as an entry point of the pipeline. This is an elegant solution for a functional programmer (F#), but for an object-oriented programmer (C#), this may cause a confusion, as this approach is not a norm and is not aligned with an object-oriented thinking even though the C# language itself supports delegate. This issue will be discussed later when talking about creating middleware using Katana, which is an implementation of OWIN in C#.

    OWIN makes a clear distinction between host, server, middleware, web framework, and web application. However, OWIN specification only deals with host, server and part of the middleware. Below is the explanation taken from OWIN website.

    1. Server — The HTTP server that directly communicates with the client and then uses OWIN semantics to process requests. Servers may require an adapter layer that converts to OWIN semantics.
    2. Web Framework — A self-contained component on top of OWIN exposing its own object model or API that applications may use to facilitate request processing. Web Frameworks may require an adapter layer that converts from OWIN semantics.
    3. Web Application — A specific application, possibly built on top of a Web Framework, which is run using OWIN compatible Servers.
    4. Middleware — Pass through components that form a pipeline between a server and application to inspect, route, or modify request and response messages for a specific purpose.
    5. Host — The process an application and server execute inside of, primarily responsible for application startup. Some Servers are also Hosts.
    (Source: http://owin.org)

    Even though it is possible to write a whole ground up web application using the middleware pipeline, that structure is, in fact, intended as an infrastructure for web development. Middleware is particularly suitable for writing web frameworks and cross cutting aspects of web applications such as error handling, logging, etc., and web applications can be written based on the web framework registered in the pipeline. Using the structure also encourages developers to split out cross cutting aspects from web applications.

  4. Web applications or web frameworks or middleware written for an OWIN implementation do not have to rely on any IIS features, and they can be plugged with any web servers which understand OWIN semantics and be hosted in different ways. OWIN compatible servers are required to implement rules specific to the server specification. Consequently, middleware can make assumptions about the availability of mandatory fields in the environment object. Middleware also need to follow rules for error or exception handling, and the order of writing response body and response headers. It is worth reading the whole OWIN specification and it does not take a long time.

The specification is just a beginning. The other goal is to encourage the development of simple and re-usable modules for .NET. The expectation is to have a signification growth of middleware that can be downloaded as nuget packages. The long-term goal is to stimulate open source ecosystem of .NET web development.

Part 2 - What is Katana

OWIN has a number of implementations such as Katana for the C# community, and Freya for the F# community. There are also a number of libraries that only implement host and server OWIN specifications such as Nowin and Suave. These lists are in the OWIN website.

Katana is the Microsoft's implementation of OWIN specification. They are in the form of nuget packages, which all have a namespace Microsoft.Owin. However, Katana is more than just an implementation of OWIN as it also has other abstractions to help the productivity in development. Katana adds a number of object-oriented constructs such as the IOwinContext interface and the OwinMiddleware abstract class. The IOwinContext is used as an abstraction for the environment object and can be instantiated as an OwinContext by taking a IDictionary<string, object> object as the argument in the constructor, while the OwinMiddleware serves as a base class for creating a middleware class. In fact, OWIN has not specified the standard form of middleware or the pipeline builder. However, from the look in the OWIN Middleware draft, only delegates are used. In contrast, being an object-oriented C# implementation of OWIN, Katana uses the AppBuilder class, which implements the interface IAppBuilder, as the pipeline builder. In addition to that, Katana allows creating middleware as a class, which can be registered using the class type or class instance by the AppBuilder. It should be assumed that these Katana's middleware classes can only be used by the AppBuilder.

Katana also has a number of constructs to help accessing the environment object such as the IOwinRequest and the IOwinResponse, which are implemented by the OwinRequest and OwinResponse classes respectively. OWIN compatible middleware can still be authored by limited only using Katana's constructs such as OwinContext, OwinRequest, OwinResponse inside the middleware body.

Moreover, Katana also contains hosting components and a listener component, Hosting components deal with the server aspects of applications such as creating listeners, or converting a received http request to an environment object complying with OWIN semantics, and passing the environment object into the middleware pipeline. When creating an OWIN application with the host together or self hosting such as creating an OWIN application running as a console application, winforms application, or windows service, both hosting and listener components are required. On the other hand, if the OWIN application is hosted with IIS, only the hosting component is required to convert a http request to an environment object and passing it into the middleware pipeline. Katana also implements a host called 'OwinHost', as a nuget package, which can be used to replace IIS. The differences between the two is that IIS has its own pipeline while 'OwinHost' does not have one. When hosted with IIS, the server or listener is in the IIS infrastructure, and the OWIN application is operated under an IIS Integrated pipeline model, where the IIS pipeline is combined with OWIN middleware pipeline, and this will be explained in the next part. All above hosting components are split into a number of different packages, so they can be used selectively to fit the application.

Lastly, Katana has a number of common middleware such as CORS, Diagnostic, Static Files, Security and helper and utility classes. Below is the important assemblies or DLLs related to Katana:

  • Microsoft.Owin - Katana implementation of OWIN
  • Microsoft.Owin.Host - Common library for hosting component
  • Microsoft.Owin.Host.HttpListener - Listener or server implementation
  • Microsoft.Owin.Host.SystemWeb - Hosting component for IIS Hosting
  • Microsoft.Owin.Diagnostic - Diagnostic middleware
  • Microsoft.Owin.Cors - Cors middleware
  • Microsoft.Owin.StaticFiles - Static files middleware
  • Microsoft.Owin.Security - Common library for security and Identity
  • Microsoft.Owin.Security.* - Middleware for specific security/identity model, e.g., Cookies, active directory, Facebook, Google, jwt, oauth, wsfederation, twitter, Microsoft Account, open ID connect
  • Owin - OWIN abstraction
  • OwinHost - Katana host implementation

These assemblies are distributed via a number of nuget packages, e.g., OwinHost, Microsoft.Owin.Host.SystemWeb, and Microsoft.Owin.Host.SelfHost. Katana implementation is open source, and the source code can be accessed from https://github.com/aspnet/AspNetKatana.

Part 3 - Application Development with OWIN Katana

As mentioned in previous part, OWIN Katana provides a structure to write web applications. Cross cutting concerns, such as logging and exception handling, and web frameworks, such as Web API, can be written as middleware. The main web application logic can be written using the web framework middleware registered in the pipeline. In this part, OWIN Katana applications development is attributed to the activity of writing middleware, creating a middleware pipeline and starting listeners or servers. Developing web applications using Web API framework will be discussed in the next part separately.

3.1. Creating Listeners or Servers

Creating listeners or servers is only required for self hosting scenarios and not a requirement for OwinHost or IIS hosting models. Katana provides a static method WebApp.Start from Microsfot.Owin.Hosting to create a server or servers. Creating servers are the same as creating listeners and activating them. There are a number of overload methods and generic methods to do this.

  1. IDisposable Start(string url, Action<IAppBuilder> startup)
  2. IDisposable Start(StartOptions startOptions, Action<IAppBuilder> startup)
  3. IDisposable Start<TStartup>(string url)
  4. IDisposable Start<TStartup>(StartOptions startOptions)
  5. IDisposable Start(StartOptions startOptions)
  6. IDisposable Start(string url)

There are two important pieces of information required when creating a listener in OWIN Katana, the listening url and the startup delegate, as shown in the first overload. The listening url is the endpoint to receive http requests, while the startup delegate, which has a signature Action<IAppBuilder>, is the function to configure a middleware pipeline.

The WebApp.Start method not only creates a listener but also joins it to a middleware pipeline. This method creates a middleware pipeline builder, of type IAppBuilder, passes to the startup delegate, to be configured, and builds the middleware pipeline to get an AppFunc entry to first middleware in the pipeline. The startup delegate can take a number of forms: method name (method groups), delegate variable, or lambda expression, as demonstrated in the code below:

C#
public class Program
{
  public static void Main(string[] args)
  {
     //1. Using an existing method (method groups)
     using (var host = WebApp.Start("http://localhost:9000", CustomConfiguration))
     {
        Console.WriteLine("Start Listening...");
        Console.ReadKey();
     }

     //2.Using a delegate variable
     Action<IAppBuilder> startup = CustomConfiguration;
     using (var host = WebApp.Start("http://localhost:9000", startup))
     {
        Console.WriteLine("Start Listening...");
        Console.ReadKey();
     }

     //2. Using a lambda expression
     using (var host = WebApp.Start("http://localhost:9000", 
            builder => builder.Use(typeof(Middleware))))
     {
        Console.WriteLine("Start Listening...");
        Console.ReadKey();
     }
  }

  public static void CustomConfiguration(IAppBuilder appBuilder)
  {
     appBuilder
       .Use(.....);  
  }
}

When running a server application, the url and particularly the port must not be used by other applications, and the user account running the application must have a permission to open the port. If this happens, the application throws an access denied exception. Run the following command prompt with an administrative privilege.

netsh http>add urlacl http://[ipaddress]:[port]/ user=Everyone

Using the second overload method, multiple listening urls can be created by setting the StartOptions's Urls property. The example code below demonstrates how to create a server which listens at multiple urls.

C#
public static void Main(string[] args)
{
   StartOptions startOptions = new StartOptions();
   startOptions.Urls.Add("http://localhost:9000");
   startOptions.Urls.Add("http://localhost:9500");
   startOptions.Urls.Add("http://localhost:9700");

   using (var host = WebApp.Start(startOptions, CustomConfiguration))
   {
     Console.WriteLine("Start Listening...");
     Console.ReadKey();
   }
   
   public static void CustomConfiguration(IAppBuilder appBuilder)
   {
     appBuilder
       .Use(.....);  
   }
}

The third and fourth generic methods is similar to first and second methods, except they accept a generic startup class instead of a startup delegate. In order to be used as a startup class for these methods, the class must not be static and has an instance or static method with the name Configuration. This method must have the same signature as the startup delegate, Action<IAppBuilder>. The code below shows an example of creating a server using a startup class.

C#
public class Program
{
  public static void Main(string[] args)
  {   
    using (var host = WebApp.Start<CustomStartup>("http://localhost:9000"))
    {
      Console.WriteLine("Start Listening...");
      Console.ReadKey();
    }
  } 
}

public class CustomStartup
{
  public static void Configuration(IAppBuilder appBuilder)
  {
    appBuilder
      .Use(.....); 
  }
}

The fifth overload method only accepts one argument, StartupOption. The startup class or delegate can be set in the StartupOption's AppStartup property. The property can take a 'static' or instance startup class name. It can also take any method name with a signature of Action<IAppBuilder>. The example code below illustrates the usage of the fifth overload method.

C#
public class Program
{
  public static void Main(string[] args)
  {
    StartOptions startOptions = new StartOptions();
    startOptions.Urls.Add("http://localhost:9000");
    startOptions.Urls.Add("http://localhost:9500");

    startOptions.AppStartup = "Owin.Application.CustomStartup";                     //1. Assign 
                                               // to a startup class name (CustomStartup class)
    startOptions.AppStartup = "Owin.Application.CustomStartup.CustomConfiguration"; //OR 2. 
                   // Assign to a method name (Configuration method of the CustomStartup class)
    using (var host = WebApp.Start(startOptions))
    {
      Console.WriteLine("Start Listening...");
      Console.ReadKey();
    }      
  } 
}

public static class CustomStartup
{
  public static void CustomConfiguration(IAppBuilder appBuilder)
  {
    appBuilder
      .Use(...);
  }
}

If AppStartup property has not been set, the method will auto detect a startup class, which is similar to what the six or final overload method, which does not accept a startup delegate or a startup class, does. The following list are the rules for auto detect a startup class:

  1. Naming convention - Katana will look for a static or instance class with the name Startup (case sensitive), and contains a static or instance method with the name Configuration in the root (global) namespace or the namespace which matches with the assembly name.
  2. OwinStartupAttribute - This method takes precedence over the naming convention. Katana will look for this attribute specified as an assembly level attribute, e.g. [Assembly:OwinStartup(typeof(CustomStartup))]. It can be declared anywhere allowed for assembly attributes. The attribute has three overload methods. The first one, like the example, takes the type of a startup class. The second ones takes both the type of a startup class and its startup method. The third one, takes a friendly name and the type of a startup class. The friendly name is to be used in conjunction with the configuration file.
  3. Configuration file - This method takes precendence over the OwinStartupAttribute method. Katana will look in the application configuration file for a key with the name owin:appStartup under the appSettings section. The key can be used to link to a startup class directly by specifying the full name of the class. Alternatively, the key can be used to link to a OwinStartupAttribute by specifying the friendly name of the attribute. Multiple OwinStartupAttributes, each with a different friendly name can be declared. Then, the configuration file can decide which one to be used in the owin:appStartup key.

The application can also control the OWIN Startup class auto detection feature explicitly using another configuration key owin:AutomaticAppStartup, by setting it to 'true' or 'false'. A detailed explanation about OWIN Startup class detection can be found in this MSDN article.

3.2. Configuring Middleware Pipeline

The second element of creating an OWIN Katana application is to configure the middleware pipeline. OWIN Katana provides a builder abstraction, IAppBuilder, to allow developers to configure the pipeline. The builder implementation AppBuilder is created inside WebApp.Start method and then it invokes the startup method with the builder as the argument. Therefore, the function of the startup method is to configure the pipeline.

The IAppBuilder utilises a Use method, which has the same signature as Action<object middleware, object[] args>, to register middleware. The middleware object, which is a dynamic type, can take a number of forms, such as delegate, class type or class instance. Additionally, middleware can optionally have one or more arguments. The code below demonstrates the registration of a Logging middleware class with a loggingOptions argument:

C#
public void Configuration(IAppBuilder appBuilder)
{
   var loggingOptions = new LoggingOptions()
   {
       Level = 1
   };

   appBuilder
     .Use(typeof(Logging), loggingOptions);
}

The Use method usage will be discussed in a more detail in the next section about creating middleware.

Moreover, Katana provides a number of extension methods for the application builder, IAppBuilder. Some important ones are:

  1. Use<T>(string[] args)
  2. Use(Func<IOwinContext, Func<Task> /*next*/, Task> handler)
  3. Run(Func<IOwinContext, Task> handler)
  4. Map(string pathMatch, Action<IAppBuilder> configuration)
  5. MapWhen(Predicate predicate, Action<IAppBuilder> configuration)
  6. MapWhenAsync(PredicateAsync predicate, Action<IAppBuilder> configuration)

The generic methods Use<T> provides an alternative way to register middleware if the middleware is implemented as a class and not a delegate. It can optionally take one or more middleware arguments. Using this method, the previous code example can be written as below:

C#
public void Configuration(IAppBuilder appBuilder)
{
   var loggingOptions = new LoggingOptions()
   {
       Level = 1
   };

   appBuilder
     .Use<Logging>(loggingOptions);
}

The second and third methods provide different ways to register middleware and they use Katana abstraction and not OWIN abstraction. Under the hook, the methods register the UseHandlerMiddleware middleware, and they take a handler as the middleware argument. A handler is equivalent to a middleware body. The second method takes a handler which accepts IOwinContext and the 'next' component (Func<Task>), while the Run method's handler only takes IOwinContext. The third method should only be used for registering the last middleware, or the middleware handler to be precise, in the pipeline. Below is the example of the usage of the second and third methods:

C#
public void Configuration(IAppBuilder appBuilder)
{
  appBuilder
     .Use(PassThroughHandler)
     .Run(TerminatingHandler);
}

public async Task PassThroughHandler(IOwinContext context, Func<Task> next)
{
    System.Console("This is an inbound flow of a pass through middleware");
    
    //the calling of below statement will call 'next' 
    //component of the underlying middleware 'UseHandlerMiddleware' 
    await next.Invoke(); 

    System.Console("This is outbound an flow of a pass through middleware");
}

public async Task TerminatingHandler(IOwinContext context)
{
    System.Console("This is a terminating middleware");
}

The fourth or Map method provides a branching strategy for the pipeline based on the path matching evaluation of the request's URI. Under the hook, it uses the MapMiddleware middleware. This middleware checks if the request's URI Path starts with the given pathMatch argument. If it matches, the middleware executes the map pipeline, which is configured by the method's second argument delegate. The middleware becomes a terminating middleware for the main pipeline. Otherwise, the middleware continues the execution to the next middleware in the main pipeline.

When the middleware execution goes into the map pipeline, the pathMatch is added to the request's URI BasePath and is reduced from request's URI Path. Moreover, when using the Map method, the pathMatch argument should not end with the '/' character. Below is an example of the Map method usage.

C#
public void Configuration(IAppBuilder appBuilder)
{
   appBuilder
      .Map("/Admin", ConfigurationForAdmin)
      .Use(typeof(MainPipelineMiddleware));
}

public void ConfigurationForAdmin(IAppBuilder appbuilder)
{
    appBuilder
      .Use(typeof(AdminPipelineMiddleware));    
}

The MapWhen and MapWhenAsync methods is similar to Map method, except that their evaluation using a predicate delegate, an expression that return a Boolean, instead of using the path matching. The MapWhenAsync in particular uses an awaitable predicate delegate. Under the hook, they use the MapWhenMiddleware middleware. If the predicate delegate returns 'true', the middleware executes the map pipeline, otherwise it continues the execution to the next middleware in the main pipeline. The predicate clause takes an IOwinContext argument, which gives the flexibility to evaluate the whole request and not just the request's URI Path. Below is an example of MapWhen and MapWhenAsync methods usage.

C#
public void Configuration(IAppBuilder appBuilder)
{
    appBuilder
      .MapWhen(IsPostMethod, ConfigurationForPostMethod)
	  .MapWhenAsync(IsGetMethodAsync, ConfigurationForGetMethod)
	  .Use(typeof(MainPipelineMiddleware));
}

public bool IsPostMethod(IOwinContext context)
{
    return context.Request.Method == "POST";
}

public async Task<bool> IsGetMethodAsync(IOwinContext context)
{
    return await Task.FromResult(context.Request.Method == "GET");
}

public void ConfigurationForPostMethod(IAppBuilder appBuilder)
{
    appBuilder
	  .Use(typeof(PostMethodPipelineMiddleware));
}

public void ConfigurationForGetMethod(IAppBuilder appBuilder)
{
    appBuilder
	  .Use(typeof(GetMethodPipelineMiddleware));
}

When using multiple Map or MapWhen in the pipeline, more specific constraints should be put first, otherwise they will never have a chance to be evaluated.

3.3. Creating Middleware

Writing Middleware is part of the OWIN Katana applications development. The Use method is the main mechanism to register middleware into the pipeline. The signature of the method is IAppBuilder Use(object middleware, params object[] args), which takes middleware object, and optional middleware arguments. All parameters are dynamic objects and the correct argument types must be provided otherwise the application will throw a runtime exception.

Middleware can be created as a delegate function or a class, and there are a number of ways to register them into the pipeline. Below are the several ways to create middleware based on the mechanism to register them into the pipeline.

3.3.1. Creating a Middleware Delegate, Registering by a Delegate

For a delegate to be qualified as a middleware object, the delegate must have a signature of Func<AppFunc, AppFunc>. The first AppFunc, or the delegate's argument type, represents the 'next' middleware component, while the second AppFunc, or the delegate's return type, is the middleware body. If the middleware also has additional arguments, known as the middleware options in the Katana terminology, the delegate signature must be Func<AppFuc, args, AppFunc>. For example, if the middleware has string and int arguments, the delegate must be Func<AppFunc, string, int, AppFunc>. The example code below illustrates how to create and register middleware into the pipeline as a delegate.

C#
namespace MiddlewareUsingDelegate
{
   using AppFunc = Func<IDictionary<string, object>, Task>;

   public class Startup
   {
      public void Configuration(IAppBuilder appBuilder)
      {
         Func<AppFunc, AppFunc> delegateMiddleware = new Func<AppFunc, 
         AppFunc>(MiddlewareMethod); //assign a delegate variable using a delegage constructor
         delegateMiddleware = MiddlewareMethod;           //OR assign a delegate variable 
                                                          //using method groups

         appBuilder
           .Use(delegateMiddleware)                       //1. using a delegate variable
           .Use((Func<AppFunc, AppFunc>)MiddlewareMethod) //2. using casting to a delegate 
                                                          //   from method groups (need casting 
                                                          //   or delegate constructor)
           .Use(new Func<AppFunc, AppFunc>((next =>       //3. using a delegate constructor  
                                                          //   taken a lambda expression (need 
                                                          //   casting or delegate constructor)
           {
              //the line here is executed one time when pipeline is built
             AppFunc middlewareMainComponent = async env => 
            {
               //the line here is executed for every http request
               Console.WriteLine("Inbound with delegate");
               await next.Invoke(env);
               Console.WriteLine("Outbound with delegate");
            };

            return middlewareMainComponent;
          })))
          .Use((Func<AppFunc, string, AppFunc>)
            MiddlewareMethodWithOptions, "Options"); //4. delegate with additional param args    
      }

      public AppFunc MiddlewareMethod(AppFunc next)
      {
         //the line here is executed one time when pipeline is built
         AppFunc middlewareBody = async (env) =>
         {
            //the line here is executed for every http request
            Console.WriteLine("Inbound with delegate");
            await next.Invoke(env);
            Console.WriteLine("Outbound with delegate");
         };

         return middlewareBody;
      }

      public AppFunc MiddlewareMethodWithOptions(AppFunc next, string options)
      {
         //the line here is executed one time when pipeline is built
         AppFunc middlewareBody = async (env) =>
        {
           //the line here is executed for every http request
           Console.WriteLine(string.Format("Inbound with delegate {0}", options));
           await next.Invoke(env);
           Console.WriteLine(string.Format("Outbound with delegate {0}", options));
        };

        return middlewareBody;
      } 
   }
}

The first three registrations basically do the same thing. The first one assigns the delegate into a variable first before passing it into the Use method. The variable can be assigned from a delegate construction or a method groups assignment or a lambda expression (not shown). The method groups and lambda expression cannot be used directly in the Use method. They need to be converted into a delegate first, either via a delegate construction or casting (2 & 3). The method 4 shows a registration of a middleware delegate with middleware options. The delegate, or middleware method, can be divided into two execution blocks. The first block is to be executed once when the pipeline is built. The second block, or the middleware body, is to be executed for every received http request.

As discussed in the previous section, Katana provides extension method Use and Run which can accept middleware handler delegates Func<IOwinContext, Func<Task>, Task> and Func<IOwinContext, Task> respectively. It is called handlers because the under the hook they already use the UseHandlerMiddleware middleware. Using these methods, these handlers can be registered as method groups or a lambda expression directly in the Use or Run methods.

3.3.2. Creating a Middleware Class, Registering by a Class Type

For a class to be qualified as a middleware object and can be registered by its class type, the class must have a constructor which at least accepts an AppFunc delegate representing 'next' middleware component. The class can have additional arguments in the constructor, and when registering the middleware, the corresponding arguments must be passed as the args parameter. Additionally, the class must have an Invoke method with a signature of Func<Task, AppFunc>. The example code below shows the registration of middleware using their class type, which can be done by using typeof operator (1). Additionally, the args parameter can be provided if the middleware has the associated constructor (2).

C#
namespace MiddlewareUsingClassType
{
   using AppFunc = Func<IDictionary<string, object>, Task>;

   public class Startup
   {
      public void Configuration(IAppBuilder appBuilder)
      {
         appBuilder
           .Use(typeof(ClassTypeMiddleware))             //1. Typeof operator
           .Use(typeof(ClassTypeMiddleware), "Options"); //2. Typeof operator, 
                                                         //   with an args parameter
      }  
    }

    public class ClassTypeMiddleware
    {
       private AppFunc _next;
       private string _option;

      public ClassTypeMiddleware(AppFunc next)
      {
         _next = next;   
      }

      public ClassTypeMiddleware(AppFunc next, string option)
      {
         _next = next;
          _option = option;
      } 

      public async Task Invoke(IDictionary<string, object> env)
      {
         Console.WriteLine(string.Format("Inbound with class type {0}", _option));
         await _next.Invoke(env);
         Console.WriteLine(string.Format("Outbound with class type {0}", _option));
      }
   }
}

Alternatively, Katana also provides its own abstraction to implement a middleware class, by deriving from the OwinMiddleware abstract class, and can be registered into the pipeline by its class type. Its constructor takes an OwinMiddleware instead of an AppFunc, and the Invoke method takes IOwinContext instead of IDictionary<string, object>. Below is an example code of creating and registering middleware using a Katana middleware class. Method 2 is an example with a middleware argument.

C#
namespace MiddlewareUsingKatanaClassType
{
   public class Startup
   {
      public void Configuration(IAppBuilder appBuilder)
      {
         appBuilder
           .Use(typeof(KatanaClassTypeMiddleware))              //1. Katana middleware class
           .Use(typeof(KatanaClassTypeMiddleware), "Options");  //2. Katana middleware class 
                                                                //   with a middleware argument
      }  
   }

   public class KatanaClassTypeMiddleware : OwinMiddleware
   {
      private string _options;

      public KatanaClassTypeMiddleware(OwinMiddleware next) : base(next)
      {
         Next = next;
      }

      public KatanaClassTypeMiddleware(OwinMiddleware next, string options) : base(next)
      {
         Next = next;
         _options = options;
      }

      public override async Task Invoke(IOwinContext context)
      {
         Console.WriteLine(string.Format("Inbound with Katana middleware {0}", _options));
         await Next.Invoke(context);
         Console.WriteLine(string.Format("Outbound with Katana middleware {0}", _options));
      }   
   }
}

As discussed in the previous section, Katana provides a generic method Use<T> as an alternative to using the typeof operator. This method can be used for all middleware classes discussed in this section. The middleware registration in the previous example can be rewritten as follows:

C#
public void Configuration(IAppBuilder appBuilder)
{
    appBuilder
    .Use<KatanaClassTypeMiddleware>()                  //1. Katana middleware class
    .Use<KatanaClassTypeMiddleware>("Options");        //2. Katana middleware class 
                                                       //   with a middleware argument
}

3.3.3. Creating a Middleware Class, Registering by a Class Instance

For a class to be qualified as a middleware object, and can be registered by its class instance, the corresponding class must either:

  • have an Initialize method which accepts an AppFunc, representing 'next' middleware component, and optionally middleware arguments, and an Invoke method with a signature of Func<IDictionary<string, object>, Task>
  • have an Invoke method with a signature of Func<AppFunc, args, AppFunc>, where args are for middleware arguments

The example codes below illustrate middleware classes with an Initialize method (1 & 2), and without an Initialize method (3 & 4). The methods 2 & 4 demonstrate middleware classes with a middleware argument. For method 2, the class must have an Initialize method with a signature of Action<AppFunc, args>. For method 4, the class must have an Invoke method with a signature of Function<AppFunc, args, AppFunc>.

C#
namespace MiddlewareUsingClassInstance
{
   using AppFunc = Func<IDictionary<string, object>, Task>;

   public class Startup
   {
      public void Configuration(IAppBuilder appBuilder)
      {
         appBuilder
           .Use(new ClassInstanceMiddlewareWithInitialize())            //1. class instance 
                                                                        //   with Initialize
           .Use(new ClassInstanceMiddlewareWithInitialize(), "Options") //2. class instance 
                                              // with Initialize having additional argument
           .Use(new ClassInstanceMiddlewareNoInitialize())              //3. class instance 
                                                                        //   No Initialize    
           .Use(new ClassInstanceMiddlewareNoInitialize(), "Options");  //4. class instance 
                                              // No Initialize having additional argument   
      }
   }

   public class ClassInstanceMiddlewareWithInitialize
   {
      private AppFunc _next;
      private string _options;

      public void Initialize(AppFunc next)
      {
         _next = next;   
      }

      public void Initialize(AppFunc next, string options)
      {
         _next = next;
        _options = options;
      }

      public async Task Invoke(IDictionary<string, object> env)
      {
         Console.WriteLine(string.Format
                 ("Inbound with class instance (with Initialize) {0}", _options));
         await _next.Invoke(env).ConfigureAwait(false);
         Console.WriteLine(string.Format("Outbound with class instance 
                 (with Initialize) {0}", _options));
      }
   }

   public class ClassInstanceMiddlewareNoInitialize
   {
      public AppFunc Invoke(AppFunc next)
      {
         return async (env) =>
         {
            Console.WriteLine(string.Format("Inbound with class instance (No Initialize)"));
            await next.Invoke(env).ConfigureAwait(false);
            Console.WriteLine(string.Format("Outbond with class instance (No Initialize)"));
         };
      }

      public AppFunc Invoke(AppFunc next, string options)
      {
         return async (env) =>
         {
            Console.WriteLine(string.Format
                    ("Inbound with class instance (No Initialize) {0}", options));
            await next.Invoke(env).ConfigureAwait(false);    
            Console.WriteLine(string.Format("Outbond with class instance 
                             (No Initialize) {0}", options));
         };
      }
   }
}

When creating middleware, it is a common pattern to create extension methods on IAppBuilder for registering the middleware. The benefit is instead of using a raw Use(object middleware, params object[] args) method, developers can use a more meaningful method UseXXX(param object[] args) or UseXXX() if the middleware does not have middleware arguments (substitute XXX with the middleware name, such as Logging, ExceptionHandling, etc.)

3.4. IIS Integrated Pipeline

As mentioned in the previous part, Katana is not just an OWIN implementation. Even though the original purpose of OWIN is to remove the application dependency from IIS, Katana provides a mechanism to host web applications with IIS. All previous examples are written from the context of self hosting. So, what is the different if OWIN Katana applications are implemented and hosted with IIS? There are at least a number of differences:

  • When hosting with IIS, the listeners and server applications are created and maintained by the IIS infrastructure. So there is no need to create listeners. However, to allow the listeners to convert http requests to OWIN semantics and auto-detect the OWIN Startup class, the OWIN library Microsoft.Owin.Host.SystemWeb is required to be in the application bin folder. This can be done in the project by referencing a nuget package Microsoft.Owin.Host.SystemWeb
  • Because there is no place to define a startup method, the application will use auto detect OWIN Startup class mechanism, which has been discussed in previous part.
  • If the application is hosted with an IIS server, e.g., not IIS Express, the application needs to run in an IIS Integrated application pool.
  • When hosting with IIS, the OWIN Katana pipeline is integrated with the IIS pipeline, thus this model is also called the IIS integrated pipeline. OWIN Katana pipeline will remain the main entry point. The difference with self hosting is the OWIN Katana pipeline can be interleaved with the IIS pipeline. Below is all the stages of the IIS pipeline.
C#
public enum PipelineStage
{
    Authenticate = 0,
    PostAuthenticate = 1,
    Authorize = 2,
    PostAuthorize = 3,
    ResolveCache = 4,
    PostResolveCache = 5,
    MapHandler = 6,
    PostMapHandler = 7,
    AcquireState = 8,
    PostAcquireState 9,
    PreHandlerExecute = 10
}

UseStageMarker method is used to tell all middleware before that marker need to be executed at the marker stage. By default, all OWIN Katana middleware is executed at the IIS PreHandlerExecute stage, as if there is such UseStageMarker at the end of the pipeline. For example, if the OWIN katana middleware pipeline is like below:

C#
public void Configuration(IAppBuilder app)
{
    app
    .Use<MiddlewareOne>()
    .UseStageMarker(PipelineStage.Authenticate);
    .Use<MiddlewareTwo>()
    .UseStageMarker(PipelineStage.PostMapHandler);
    .Use<MiddlewareThree>()
    .Run<MiddlewareFour>();
}

Above OWIN Katana middleware will be combined with the IIS pipeline and sorted as follows:

  1. Authenticate stage: MiddlwareOne, IIS features
  2. PostAuthenticate stage: IIS features
  3. Authorize stage: IIS features
  4. PostAuthorize stage: IIS features
  5. ResolveCache stage: IIS features
  6. PostResolveCache stage: IIS features
  7. MapHandler stage: IIS features
  8. PostMapHandler stage: MiddlewareTwo, IIS features
  9. AcquireState stage: IIS features
  10. PostAcquireState stage: IIS features
  11. PreHandlerExecute stage: MiddlewareThree, MiddlewareFour
  12. ExecuteRequestHandler phase : IIS Features Only (No OWIN middleware here)

IIS features represent IIS middleware that can be configured through IIS or web configuration and may or may not be active at the time of execution. Because the IIS integrated pipeline model follows IIS stages in their execution, the stage marker should be placed in the correct order in the pipeline configuration. If some of the stage marker is out of order it wil be ignored and no exception will be thrown. A more detail explanation can be found in the following MSDN article.

Morever, OWIN middleware can only be placed up to the PreHandlerExecute stage, and if the last middleware in the OWIN pipeline calling next middleware, it will continue with IIS Features (e.g DirecttoryListingModule, etc). If developers are not aware, this may result in an expected behaviour, most likely an HTTP Error 403.14 - Forbidden error. The solution to this is to make sure the last middleware in the OWIN pipeline is a terminating middleware, which is not calling the next middleware. Otherwise, the IIS features, such as static files or directory listing, need to be activated so they will not fail.

Part 4 - Hosting OWIN Katana Application

When developing an OWIN Katana application, you will need to decide how to run or host the application. An OWIN Katana application can run as a console application, desktop application (e.g., WinForms application), or Windows service. The application is responsible for creating the server or the listener, and this hosting scenario is called self hosting. Alternatively, the application can be hosted with the IIS as a web application, or with a more lightweight OwinHost. All these hosting scenarios will be explored in this part. The projects are created using the Visual Studio Enterprise 2017 Update 3. Most of the project templates will have the target framework in the name (e.g., .NET Framework, .NET Core, .NET Standard). Lower version Visual Studios normally do not have the target framework in their project template names, and target .NET Framework by default. OWIN Katana has a dependency on .NET Framework, thus all projects in this part target .NET Framework.

4.1. Self Hosting as a Console Application

Self hosting as a console application, the listener or server can start when the program starts or when is triggered by a command line interaction. The project below demonstrates the former one.

  1. Create a Console App (.NET Framework) project, and give a name Owin.SelfHosting.Console . Use an equivalent project template if using a different version of Visual Studio.
  2. Add a reference to the nuget package Microsoft.Owin.SelfHost, which will add following references to the project.
    • Owin
    • Microsoft.Owin
    • Microsoft.Owin.Diagnostic
    • Microsoft.Owin.Hosting
    • Microsoft.Owin.Host.HttpListener
    • Microsoft.Owin.Host.SelfHost
  3. Create a WelcomeOptions class in a separate file. This class is used later in the WelcomeMiddleware class.
    C#
    namespace Owin.SelfHosting.Console
    {
       internal class WelcomeOption
       {
          public string HostName { get; set; }
          public string Welcome { get; set; }
    
          public WelcomeOption(string hostName, string welcome)
          {
             HostName = hostName;
             Welcome = welcome;
          }
       }
    }
  4. Create a WelcomeMiddleware class in a separate file. This class is used later in the AppBuilderExtension class.
    C#
    using System;
    using System.Threading.Tasks;
    using Microsoft.Owin;
    
    namespace Owin.SelfHosting.Console
    {
       internal class WelcomeMiddleware : OwinMiddleware
       {
          private readonly WelcomeOption _option;
    
          public WelcomeMiddleware(OwinMiddleware next, WelcomeOption option) : base(next)
          {
             _option = option;
          }
    
          public override async Task Invoke(IOwinContext context)
          {
             System.Console.WriteLine
                    ("Http request received at " + DateTime.UtcNow.ToString());
             await Next.Invoke(context);
             string welcome = string.Format("I am {0}. {1}{2}", 
                              _option.HostName, _option.Welcome, Environment.NewLine);
             await context.Response.WriteAsync(welcome).ConfigureAwait(false);
          }
       }
    }
  5. Create an AppBuilderExtensions class in a separate file. This class contains extension methods which allow developers to register the WelcomeMiddleware using the UseWelcome method instead of the generic Use method. The first method takes no argument and calls the second method by passing a default value.
    C#
    namespace Owin.SelfHosting.Console
    {
       internal static class AppBuilderExtensions
       {
          public static IAppBuilder UseWelcome(this IAppBuilder appBuilder)
          {
             return appBuilder.UseWelcome(new WelcomeOption("Peter", "Welcome to this site"));
          }
    
          public static IAppBuilder UseWelcome
                 (this IAppBuilder appBuilder, WelcomeOption option)
          {
             return appBuilder
               .Use(typeof(WelcomeMiddleware), option);
          }
       }
    }
  6. Create a Startup class in a separate file. This class is required when creating a listener.
    C#
    namespace Owin.SelfHosting.Console
    {
       internal class Startup
       {
          public void Configuration(IAppBuilder appBuilder)
          {
             appBuilder
               .UseWelcome();
          }
       }
    }
  7. In the Program class, put the code below:
    C#
    using Microsoft.Owin.Hosting;
    
    namespace Owin.SelfHosting.Console
    {
       internal class Program
       {
          public static void Main(string[] args)
          {
             string hostUrl = "http://localhost:9000/console";
             using (WebApp.Start<Startup>(""))
             {
                System.Console.WriteLine
                       (string.Format("Start Listening at {0} ...", hostUrl));
                System.Console.ReadKey();
             }
          }
       }
    }
  8. Run the application, and wait until it displays 'Start Listening at http://localhost:9000/console ...'
  9. To test the application, run any browsers and type 'http://localhost:9000/console'
  10. The browser should display 'I am Peter. Welcome to this site' and the console application should display 'Http request received at [request timestamp]'

4.2. Self Hosting as a WinForms Application

Self hosting as a winforms application, the server or listener can start when the program starts or when is trigged by an UI interaction. The project below demonstrates the latter one. The application displays a form containing a Start button and a Textbox showing server activity logs. The server starts when the Start button is clicked. The only difference with self hosting as a console application is the code, which is previously in the Program class, is moved to the event handler of Start button.

  1. Create a Windows Forms App (.NET Framework) project and give a name Owin.SelfHosting.WinForms. Use an equivalent project template if using a different version of Visual Studio.
  2. Rename Form1 to Main, and change the Text property of the form to 'Main'.
  3. In the Main form, create a button btnStart with Text set to 'Start', and create a Textbox txtLog with Multiline set to 'True'.
  4. Follow step 2 to step 6 from 4.1. Self hosting as a Console Application but change the namespace to Owin.SelfHosting.WinForms.
  5. Create a ControlWriter class in a separate file. The function of this class is to redirect System.Console output to a UI control, which is the txtLog for this project.
    C#
    using System;
    using System.IO;
    using System.Text;
    using System.Windows.Forms;
    
    //modified from answer to 
    //https://stackoverflow.com/questions/18726852/redirecting-console-writeline-to-textbox
    namespace Owin.SelfHosting.WinForms
    {
       public class ControlWriter : TextWriter
       {
          private Control textbox;
    
          public ControlWriter(Control textbox)
          {
             this.textbox = textbox;
          }
      
          public override void Write(string value)
          {
             if (textbox.InvokeRequired)
             {
                textbox.Invoke(new Action<char>(Write), value);
             }
             else
             {
                textbox.Text += value;
             }
          }
    
          public override Encoding Encoding
          {
             get { return Encoding.ASCII; }
          }
       }
    }
  6. In the btnStart click event handler, put the code below. The btnStart button functions as a switch to start or stop the listener. When it starts, it also redirects the System.Console output to the txtLog.
    C#
    private IDisposable _server;
    private void btnStart_Click(object sender, EventArgs e)
    {
       if (btnStart.Text == "Start")
       {
          txtLog.Clear();
          System.Console.SetOut(new ControlWriter(txtLog));
    
          string hostUrl = "http://localhost:9000/winforms";
          _server = WebApp.Start<Startup>(hostUrl);
          System.Console.WriteLine(string.Format("Start listening at {0} ...", hostUrl));
          btnStart.Text = "Stop";
        }
        else
        {
          _server.Dispose();
          btnStart.Text = "Start";
          txtLog.Clear();
        }
    }
  7. Run the application, and click the Start button, and wait until the txtLog shows 'Start Listening at http://localhost:9000/winforms ...'
  8. To test the application, run any browsers and type'http://localhost:9000/winforms'
  9. The browser should display 'I am Peter. Welcome to this site' and the txtLog should show 'Http request received at [request timestamp]'

4.3. Self Hosting as a Windows Service

In order to self hosting as a Windows service, the listener is created when the service starts, and disposed when the service stops.

  1. Create a Console App(.NET Framework) project and give a name Owin.SelfHosting.WindowsService. Use an equivalent project template if using a different version of Visual Studio.
  2. Follow step 2 to step 6 from 4.1. Self Hosting as a Console Application. but change the namespace to Owin.SelfHosting.WindowsService.
  3. Add a reference to the Framework Assembly System.ServiceProcess.
  4. Add a new item of type 'Windows Service', and give it a name OwinService. This will create a OwinService class in a separate file. Modify it with codes below:
    C#
    using System;
    using System.ServiceProcess;
    using Microsoft.Owin.Hosting;
    
    namespace Owin.SelfHosting.WindowsService
    {
       partial class OwinService : ServiceBase
       {
          private IDisposable _server;
    
          public OwinService()
          {
             InitializeComponent();   
          }
    
          protected override void OnStart(string[] args)
          {
             string hostUrl = "http://localhost:9000/windowsservice";
             _server = WebApp.Start<Startup>(hostUrl);
          }
    
          protected override void OnStop()
          {
            _server.Dispose();
          }
       }
    }
  5. Double click the OwinService in the solution explorer to show a designer form, right click on the designer form and select 'Add Installer' and a ProjectInstaller class will be created
  6. Double click the ProjectInstaller in the solution explorer to show a designer form, and there will be two components: serviceProcessIntaller1 and serviceInstaller1
  7. Set serviceProcessInstaller1's Account property to 'LocalSystem'
  8. Make sure serviceInstaller1's ServiceName is set to 'OwinService'. Optionally, the serviceInstaller1's DisplayName and StartType can be set for the windows service's preferences.
  9. Compile the project, and locate the output directory. By default, it should be in 'bin/Debug' or 'bin/Release' folder.
  10. Run 'installUtil.exe Owin.SelfHosting.WindowsService.exe' to register OwinService. The InstallUtil can be found in the .NET Framework folder %WINDIR%\Microsoft.NET\Framework[64]\v[framework_version]. In my machine, the location of this file is 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\'.
  11. Check the services list or type 'Services.msc' in the command prompt and find the 'OwinService', select and start it.
  12. To test the service, runs any browsers and type 'http://localhost:9000/windowsservice'.
  13. The browser should display 'I am Peter. Welcome to this site'

4.4. IIS Hosting

  1. Create an ASP.NET Web Application (NET. Framework) project, and select an Empty template, and give a name Owin.IISHosting. Use an equivalent project template if using a different version of Visual Studio.
  2. Add a reference to the nuget package Microsoft.Owin.Host.SystemWeb, which will add following references to the project:
    • Owin
    • Microsoft.Owin
    • Microsoft.Owin.Host.SystemWeb
  3. Follow step 3 to step 6 from Self Hosting as a Console Application, but change the namespace to Owin.IISHosting, and with a modification to WelcomeMiddleware class to make sure it does not call the next middleware.
    C#
    using System;
    using System.Threading.Tasks;
    using Microsoft.Owin;
    
    namespace Owin.IISHosting
    { 
       internal class WelcomeMiddleware : OwinMiddleware
       {
          private readonly WelcomeOption _option;
    
          public WelcomeMiddleware(OwinMiddleware next, WelcomeOption option) : base(next)
          {
             _option = option;
          }
    
          public override async Task Invoke(IOwinContext context)
          {
             System.Console.WriteLine
                    ("Http request received at " + DateTime.UtcNow.ToString());
             //await Next.Invoke(context); //For IIS Integrated pipeline, 
             //the last middleware should be non passthrough
             string welcome = string.Format("I am {0}. {1}{2}", 
                              _option.HostName, _option.Welcome, Environment.NewLine);
             await context.Response.WriteAsync(welcome).ConfigureAwait(false);
          }
       }
    }
  4. Open the project properties in the designer view, in the tab 'Web', in the section 'Server', check 'apply server settings to all users (store in project file)', select 'IIS Express' from the combo box, and type 'http://localhost:9000/iishosting' in the 'Project Url'.
  5. Run the application from the Visual Studio, the IIS Express will start and host the application. You can control the applications run by the IIS Express by clicking the its icon in the system tray.
  6. The Visual Studio will also start a browser and pointing at 'http://localhost:9000/iishosting' and it should display 'I am Peter. Welcome to this site'.

4.5. Hosting with an OwinHost

Katana also ships a nuget package OwinHost containing an implementation of an alternative host, which can be used to replace the IIS host. When you create an ASP Web Application project and add this package, it will add a custom host called 'OwinHost' to the project. The application then can be configured to run using the 'Owin Host' instead of the 'IIS Express'.

  1. Follow step 1 to step 3 from the 4.4. IIS Hosting, but change the namespace to Owin.OwinHosting.
  2. Install the nuget package OwinHost (3.1.0), this will add a reference to the package and add 'Servers' entry in the 'WebProjectProperties' in the project file (.csproj). This essential adds the 'OwinHost' as a custom host.
  3. Open the project properties in the designer view, in the tab 'Web', in the section 'Server', check 'apply server settings to all users (store in project file)', select the 'OwinHost' from the combo box, and type 'http://localhost:9000/owinhosting' in the 'Project Url'.
  4. Run the application from the Visual Studio, and the 'OwinHost' should start and host the application.
  5. The Visual Studio will also start a browser and pointing at 'http://localhost:9000/owinhosting' and it should display 'I am Peter. Welcome to this site'.

When hosting with the OwinHost, the nuget package Microsoft.Owin.SelfHost can be used instead of using the Microsoft.Owin.Host.SystemWeb package. The OwinHost host can work with both listeners in the Microsoft.Owin.Host.SystemWeb and in the Microsoft.Owin.Host.HttpListener (which is in Microsoft.Owin.SelfHost package).

Part 5 -Web API Development with OWIN Katana

While it is possible to develop a RESTful API with OWIN Katana, it does not give enough leverage or abstraction to do it effectively. Writing middleware in OWIN Katana is best for web frameworks or cross cutting concerns of web applications. When developing a RESTful API, it is best to use web frameworks such as Nancy or Microsoft's Web API, which will be used in this part.

Web API can be used for both ASP.NET applications and OWIN Katana applications. The benefit of developing using OWIN Katana is the flexibility to choose from various hosting scenarios as explained in the previous part. ASP.NET (up to 4.6) applications can only be hosted with the IIS. Another main differences between the two is entry point to register the Web API middleware. For OWIN Katana applications, it is registered in a startup method or a Startup class, while for ASP.NET applications, it is registered in the Application_Startup method of the Global.asax. However, ASP.NET 5, which later called ASP.NET Core, applications already use a Startup class, which follow OWIN Katana 'style'.

When implementing Web API with OWIN Katana, Web API is just a middleware in the pipeline, which can be registered by UseWebApi method, taking one argument HttpConfiguration. Web API provides high level constructs to work with when developing an API such as: controller, router, formatter, query parsing model binding, and most of them can be configured via HttpConfiguration.

The following link shows the pipeline structure inside Web API (Web API 2.0). The first stage of WebAPI is HttpMesageHandlers containing three type of handlers. Delegating handlers, similar to middleware, can be injected via the HttpConfiguration. The HttpRoutingDispatcher handler supports pipeline branching, which can be configured via HttpConfiguration. The last handler, HttpControllerDispatcher routes a request to a correct controller. Web API controller must derive from a ApiController class.

The second stage of WebAPI is a controller stage. A lot of processing takes place before a controller method is executed. Authentication and Authorization filters are executed if present. Next, Model binding attempts to map values from a request (URI, headers and body) to parameters, which can be passed into a controller. Then, Action filters, if present, are executed before the controller method. On the way out, Action filters, Result Conversion, and Exception filters are executed. In the following section, simple Web API projects will be created with self hosting and IIS hosting.

5.1. Web API with Self Hosting

Web API self hosting as a console application.

  1. Create a Console App (.NET Framework) project, give it a name Owin.WebApi.SelfHosting. Use an equivalent project template if using a different version of Visual Studio.
  2. Install the nuget package Microsoft.AspNet.WebApi.OwinSelfHost (3.1.0), which will add following references:
    • Microsoft.AspNet.WebApi.Client
    • Microsoft.AspNet.WebApi.Core
    • Microsoft.AspNet.WebApi.Owin
    • Microsoft.AspNet.WebApi.OwinSelfHost
    • Microsoft.Owin
    • Microsoft.Owin.Host.HttpListener
    • Microsoft.Owin.Hosting
    • Newtonsoft.Json
    • Owin
    Alternatively, install the nuget packages Microsoft.AspNet.WebApi.Owin (5.2.3) and Microsoft.Owin.SelfHost (3.1.0), which will add following references:
    • Microsoft.AspNet.WebApi.Client
    • Microsoft.AspNet.WebApi.Core
    • Microsoft.AspNet.WebApi.Owin
    • Microsoft.Owin
    • Microsoft.Owin.Diagnostics
    • Microsoft.Owin.Host.HttpListener
    • Microsoft.Owin.Hosting
    • Microsoft.Owin.SelfHost
    • Newtonsoft.Json
    • Owin
  3. Create a Startup class in a separate file. The startup method will register Web API with the configured HttpConfiguration. The MapHttpAttributeRoutes means the API routes based on the route attributes on the controllers.
    C#
    using System.Web.Http;
    namespace Owin.WebApi.SelfHosting
    {
       internal class Startup
       {
          public void Configuration(IAppBuilder appBuilder)
          {
             var httpConfig = new HttpConfiguration();
             httpConfig.MapHttpAttributeRoutes();
             httpConfig.EnsureInitialized();
    
             appBuilder
               .UseWebApi(httpConfig);
          }
       }
    }
  4. Create a UserController by deriving from a ApiController class. Mark the class with a RoutePrefix attribute, and the method with Route and HttpGet attributes. The route for this call is GET '/User/1234'. The Model Binding will assign the 'identifier' with the value after '/User'. This method returns a json response if the identifier = 1234, otherwise it returns a NotFound response.
    C#
    using System.Web.Http;
    
    namespace Owin.WebApi.SelfHosting
    {
       [RoutePrefix("User")]
       public class UserController : ApiController
       {
          [Route("{identifier}")]  
          [HttpGet]
          public IHttpActionResult GetUser(string identifier)
          {
             if (identifier == "1234")
             {
                return Json(new 
                {
                   Givenname = "Peter",
                   Surname = "Smith",
                   Age = 45
                });
             }
    
             return NotFound();
          }
       } 
    } 
  5. Create a Program class in a separate file.
    C#
    using System;
    using Microsoft.Owin.Hosting;
    using Owin.WebApi.SelfHosting;
    
    namespace Owin.WebApi.SelfHost
    {
       internal class Program
       {
          public static void Main(string[] args)
          {
             string hostUrl = "http://localhost:9000/webapi/selfhosting";
             using (var server = WebApp.Start<Startup>(hostUrl))
             {
                Console.WriteLine(string.Format("Start Listening at {0} ...", hostUrl));
                Console.ReadKey();
             }
          }
       }
    }
  6. Run the application, wait until it displays 'Start Listening at http://localhost:9000/webapi/selfhosting ...'
  7. To test the application, run any browsers and type 'http://localhost:9000/webapi/selfhosting/user/1234'
  8. The browser should display a json response containing user detail

5.2. Web API with IIS Hosting

  1. Create an ASP.NET Web Application (.NET Framework) project, choose an Empty template. Use an equivalent project template if using a different version of Visual Studio.
  2. There are two nuget packages to install. First, add the Microsoft.Owin.Host.SystemWeb package, which will add the following references:
    • Microsoft.Owin
    • Microsoft.Owin.Host.SystemWeb
    • Owin
  3. Second, add the Microsoft.AspNet.WebApi.Owin package, which will add the following references:
    • Microsoft.AspNet.WebApi.Client
    • Microsoft.AspNet.WebApi.Core
    • Microsoft.AspNet.WebApi.Owin
    • Newtonsoft.Json
  4. Follow step 3 and step 4 from 5.2. Web API with Self Hosting, but change the namespace to Owin.WebApi.IISHosting
  5. Open the project properties in a designer view, in the tab 'Web', section 'Server', check 'apply server settings to all users (store in project file)', select 'IIS Express' from the combo box, and type 'http://localhost:9000/webapi/iishosting' in the 'Project Url'.
  6. To test the application, run any browsers and type 'http://localhost:9000/webapi/iishosting/user/1234'.
  7. The browser should display a json response containing a user detail.

Part 6 - From OWIN Katana to ASP.NET Core

A lot of changes were happening in .NET web development technology in recent years. In line with the web developments in other languages, the tendency is to develop smaller, focused components which were released more frequently.

OWIN is the beginning and the foundation of architectural design toward more modular, lightweight, open platform and open source Microsoft web products. Katana is the Microsoft attempt to provide an implementation which developer can experience with. Feedbacks from the Katana implementation and usages were fed into the development of next Microsoft web product, ASP.NET 5.

ASP.NET 5 was first released in 2015. It combines the best features from the Web API and MVC 5 into a single product called MVC6. At the same time, Microsoft also introduced a new framework called .NET Core 5, which also targeted other platforms such as Linux and MacOS. The .NET Core Framework is a strip down version .NET Framework and is distributed as a collection of nuget packages. ASP.NET 5 can run on both .NET Core 5 and .NET Framework.

In the beginning of January 2016, the ASP.NET 5 name was changed to ASP.NET Core, in a move to make a distinction from the previous Microsoft ASP.NET 4.6 which targets .NET Framework only. At the moment, ASP.NET Core 2 product was released in August 2017 alongside with .NET Core 2 Framework, and supported in Visual Studio 2017 update version 3. However, the .NET Core 2 Framework need to be downloaded separately.

ASP.NET Core natively supports OWIN , but it does not support the Katana implementation which is .NET Framework dependant. Even though ASP.NET Core follows the same concepts in OWIN and Katana, it has its own abstraction. For example, it uses an IApplicationBuilder instead of an IAppBuilder, an HttpContext instead of IDictionary<string, object>.

ASP.NET Core 2 can register OWIN middleware by using the UseOwin method from the Microsoft.AspNetCore.Owin nuget packages. Using this method only a pure OWIN middleware, in the form of delegate Func<AppFunc, AppFunc>, can be added into the pipeline. As shown in the code below, the delegate argument pipeline is used to register the OWIN Middleware.

C#
using AppFunc = Func<IDictionary<string, object>, Task>;
...
public void Configure(IApplicationBuilder appBuilder)
{
    appBuilder
      .UseOwin(pipeline=>
        {     
            pipeline(OwinMiddlewareOne);
            pipeline(OwinMiddlewareTwo);
        });
}   

public AppFunc OwinMiddlewareOne(AppFunc next)
{
    AppFunc middlewareBody = async (env) =>
    {
        ...
        await next.Invoke(env);
        ... 
    }

    return middlewareBody;
}

public AppFunc OwinMiddlewareTwo(AppFunc next)
{
    AppFunc middlewareBody = async (env) =>
    {
        ...
        await next.Invoke(env);
        ... 
    }

    return middlewareBody;
}

In order to fully reuse OWIN Katana middleware, an extension method in the following code project article can be used. However, this will require a dependency on the .NET Framework, as Katana is .NET Framework dependant.

There are also a number changes and departure from OWIN Katana and Web API implementation such as:

  • Starting up server or listener change from using static method WebApp.Start to using IWebHost's Run() or Start(). It has a WebHostBuilder to build the object before running it. The UseUrls and UseStartup<T> are used to specify the listener addresses and a Startup class respectively. The WebHostBuilder also has many other methods. For example, the builder can specify the hosting model, such as integration with IIS via UseIISIntegration, Configuration via UseConfiguration, Server type via UseServer, Environment via UseEnvironment, Content Root or Web Root directory via UseContentRoot and UseWebRoot or capture an error and display an error page by default via CaptureStartupErrors. In the ASP.NET Core 2, the WebHost has a static method CreateDefaultBuilder, which automatically configures to use the Kestrel listener (to allow cross platform), load a configuration, set content root to Directory.GetCurrentDirectory
    C#
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }
    
        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
  • The Startup class changes. The IAppBuilder is replaced with the IApplicationBuilder. The Startup class now has two methods. The first method is void ConfigureServices(IServiceCollection services), which is basically an IOC (Inversion of Control) container, where all dependencies are injected. The second method is void Configure(IApplicationBuilder app), which is for registering middleware. Additionally, the Startup class can also takes an optional IHostingEnvironment and/or an optional ILoggerFactory arguments in the Configure method or the in the Startup constructor. To use the Web API framework, the service need to be added first by calling the AddMvc in the first method, and then it can be used by calling the UseMvc in the second method. Also, the HttpConfiguration is replaced with a delegate which accepts an IRouteBuilder argument.
    C#
    public class Startup
    {
       public void ConfigureServices(IServiceCollection services)
       {
          services.AddMvc();
       }
       
       public void Configure(IApplicationBuilder appBuilder, 
              IHostingEnvironment env, ILoggerFactory loggerFactory)
       {
          loggerFactory
            .AddConsole();
    
          appBuilder
            .UseMvc(routeBuilder =>
            {
               routeBuilder.MapRoute(
                 name: "default",
                 template: "{controller=Home}/{action=Index}/{id?}");
            };   
       }
    }
  • The middleware class has the same structure as the native OWIN middleware class. However, it uses a HttpContext instead of a IDictionary<string, object> and the 'next' component is a delegate called RequestDelegate instead of a Func<IDictionary<string, object>, Task>. The HttpContext is not the old System.Web object, but a new lightweight object. The object can be converted to an OWIN environment object by using the OwinEnvironment class from the Microsoft.AspNetCore.Owin package. The RequestDelegate is equivalent to Func<HttpContext, Task>. Here is the example of the new middleware class.
    C#
    public class RequestCultureMiddleware
    {
       private readonly RequestDelegate _next;
    
       public RequestCultureMiddleware(RequestDelegate next)
       {
          _next = next;
       }
    
       public Task Invoke(HttpContext context)
       {
          //Inbound processing
          return this._next(context);
          //Outbound processing
       }
    }

Overall, ASP.NET core applies core concepts laid out in the OWIN, even though it departs from the Katana implementation. Note: all projects for Part 4 - OWIN Katana Hosting Application and Part 5 - Web API Development with OWIN Katana can be downloaded from github.

References

  1. OWIN website
  2. Microsoft ASP.NET Web API (2.0) Poster
  3. OWIN Startup class detection
  4. Using OWIN pipeline in ASP.NET Core
  5. Katana, ASP.NET 5, and bridging the gap
  6. Katana source files

History

  • 19th September, 2017: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) EMIS
United Kingdom United Kingdom
(You can visit my blog in

Comments and Discussions

 
QuestionGreat (5) Pin
peterkmx18-Sep-20 22:40
professionalpeterkmx18-Sep-20 22:40 
GeneralMy vote of 5 Pin
Steffen Ploetz18-Sep-20 21:58
mvaSteffen Ploetz18-Sep-20 21:58 
GeneralMy vote of 5 Pin
san2debug24-Oct-17 18:36
professionalsan2debug24-Oct-17 18:36 
BugSmall Typo Pin
Ehsan Sajjad16-Oct-17 7:02
professionalEhsan Sajjad16-Oct-17 7:02 
GeneralRe: Small Typo Pin
Ehsan Sajjad16-Oct-17 7:17
professionalEhsan Sajjad16-Oct-17 7:17 
GeneralRe: Small Typo Pin
kusnaditjung21-Oct-17 0:41
kusnaditjung21-Oct-17 0:41 
GeneralRe: Small Typo Pin
Member 271004223-Oct-17 11:29
professionalMember 271004223-Oct-17 11:29 
GeneralRe: Small Typo Pin
Ehsan Sajjad24-Oct-17 0:57
professionalEhsan Sajjad24-Oct-17 0:57 
PraiseBrilliant article Pin
sunneku6-Oct-17 8:44
sunneku6-Oct-17 8:44 
GeneralRe: Brilliant article Pin
kusnaditjung25-Oct-17 14:05
kusnaditjung25-Oct-17 14:05 
QuestionExcellent Pin
Rob Grainger27-Sep-17 21:58
Rob Grainger27-Sep-17 21:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.