-
Notifications
You must be signed in to change notification settings - Fork 66
OWIN middleware
Information on Simple.Web's OWIN and Katana support can be found here
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.
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
.
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 ;-)
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>";
}
}
}