5 Days of Simple.Web: OWIN Middleware

OWIN middleware

Having already blogged about Simple.Web’s OWIN and Katana support I’d like to concentrate on the exciting bit – the middleware! First, one quick note of clarification;

OWIN is a standard interface specification born into the community by Benjamin van der Veen ~2010 to provide a common “glue” between server, and framework or module. Katana is Microsoft’s own implementation of an OWIN-compatible web server with accompanying set of modules.

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.

1
2
3
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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
1
2
3
4
5
6
7
8
9
10
11
<!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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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>"; } }
    }
}

Note The NancyFx integration shown here requires their next release (v0.20) which I believe is imminent. In the meantime use their bleeding edge feed.

[NancyFX] NancyIndex.cs
1
2
3
4
5
6
7
8
9
10
namespace OwinMiddleware
{
    public class SimpleModule : Nancy.NancyModule
    {
        public SimpleModule()
        {
            Get["/nancy"] = _ => "<h1>Hello from Nancy!</h1>";
        }
    }
}

Thanks!

This brings me to the end of this blog series on Simple.Web. I’m confident you too agree Mark Rendle has created something beautifully simple and elegantly powerful, whilst remaining unopinionated in its usage.

.. And that really is all for now folks! :–)

Comments