Skip to content
Diego Fernandez edited this page Mar 20, 2014 · 2 revisions

Information on Simple.Web's OWIN and Katana support can be found here

Introduction

OWIN is gaining traction in the .NET community with exciting alternatives on the horizon; however for the purposes of this post I'm going to stick with Katana and it's middleware modules given my confidence around stability of the mono-compatible branch and nuget feed I maintain with Dale at @monkeysquareorg.

Middleware

When we talk about "middleware" we are really talking about modules that provide functionality to your request pipeline. Not only does this promote good separation and usability, it gives opportunity to request handling and pipeline composition.

The ability to interface a module, or a framework like Simple.Web, into the pipeline is supported by a delegate as defined in the OWIN interface specification.

using AppFunc = Func<
        IDictionary<string, object>, // Environment
        Task>; // Done

IDictionary<string, object> contains environment data relating to the request, outgoing response, and server state. Futhermore modules can add their own keys that can then be consumed by subsequent modules in the chain. The module must return a Task that eventually completes or throws an Exception.

Composition

Below is a fantastical example of how you could compose modules.

OWIN +
     |- Custom Tracer // Custom module for tracing
     |
     |- Error Handling // Renders intercepted exceptions (prettily)
     |
     |- Cross-Origin (CORS) // Handles cross-origin resource sharing
     |
     |- Authentication // Authenticates the incoming request
     |
     |- Static-file Serving // Serve any static files
     |
     `- Custom Routing
         |
         |- Simple.Web application // Serve pages with Simple.Web
         |
         |- NancyFX application // Serve pages with NancyFX
         |
         `- IIS pass-through** // Pass requests to IIS backend

And YES! I really just did mix Simple.Web, NancyFX, and IIS in the same pipeline ;-)

Example

For simplicity I'm leaving out the Authentication and IIS pass-through from our fantastical example above.

[Bootstrap] Program.cs

namespace OwinMiddleware
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Threading.Tasks;

    using Microsoft.Owin.Cors;

    using Nancy;
    using Nancy.Owin;

    using Owin;

    using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;

    internal class Program
    {
        private static void Main(string[] args)
        {
            new Simple.Web.Hosting.Self.OwinSelfHost(
                builder =>
                {
                    builder.Use(typeof(CustomTracer));

                    builder.UseErrorPage();

                    builder.UseCors(CorsOptions.AllowAll);

                    builder.UseStaticFiles("/static", "static");

                    // Let's hack a custom routing module
                    builder.Use(
                        new Func<AppFunc, AppFunc>(
                            ignoreNextApp => (env =>
                                {
                                    var requestPath = (string)env["owin.RequestPath"];

                                    // Normally we'd just do `builder.UseSimpleWeb()`
                                    if (requestPath.StartsWith("/simple"))
                                        return Simple.Web.Application.Run(env);

                                    // Normally we'd just do `builder.UseNancy()`
                                    if (requestPath.StartsWith("/nancy"))
                                        return new NancyOwinHost(ignoreNextApp, new NancyOptions()).Invoke(env);

                                    var tcs = new TaskCompletionSource<object>();
                                    tcs.TrySetException(new Exception("Something went wrong!"));

                                    return tcs.Task;
                                })));
                }).Run();
        }
    }

    public class CustomTracer
    {
        private readonly AppFunc next;

        public CustomTracer(AppFunc next)
        {
            this.next = next;
        }

        public Task Invoke(IDictionary<string, object> env)
        {
            var requestPath = (string)env["owin.RequestPath"];

            Trace.WriteLine(
                string.Format("Received request for: {0}", requestPath));

            // We must pass-through onto the next module
            return this.next(env);
        }
    }
}

[Static Page] static\Stuff.html

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>I am staticly served!</title>
</head>
<body>
    <h1>Welcome to an OWIN statically served page :-)</h1>
</body>
</html>

[Simple.Web] SimpleIndex.cs

namespace OwinMiddleware
{
    using Simple.Web;
    using Simple.Web.Behaviors;

    [UriTemplate("/simple")]
    public class SimpleIndex : IGet, IOutput<RawHtml>
    {
        public Status Get()
        {
            return 200;
        }

        public RawHtml Output { get { return "<h1>Hello from Simple.Web!</h1>"; } }
    }
}

[NancyFX] NancyIndex.cs

namespace OwinMiddleware
{
    public class SimpleModule : Nancy.NancyModule
    {
        public SimpleModule()
        {
            Get["/nancy"] = _ => "<h1>Hello from Nancy!</h1>";
        }
    }
}