Microsoft.Owin, a.k.a. Katana, provides a number of very useful abstractions for working with OWIN. These types are optional but greatly ease the construction of applications and middleware, especially security middleware. The Katana team did a great job of ensuring their implementations work well with Microsoft.Owin as well as the standard OWIN MidFunc described below. However, not all third-party middleware implementations followed the conventions set forth in the Katana team’s implementations. To be fair, OWIN only standardized the middleware signature earlier this year, and the spec is still a work in progress.
At some point in the last year, I discovered the HttpMessageHandlerAdapter was not, in fact, OWIN compliant. I learned from the Web API team they would not be able to fix the problem without introducing breaking changes, so I came up with an adapter that should work with most implementations based on OwinMiddleware that didn’t expose the OWIN MidFunc.
The solution is rather simple, and the Katana source code provides one of the necessary pieces in its AppFuncTransition class. The adapter wraps the OwinMiddleware-based implementation and exposes the OWIN MidFunc signature. After creating the adapter specifically for HttpMessageHandlerAdapter, I created a generic version I think should work for any OwinMiddleware. (Examples in both C# and F#.)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Threading.Tasks; | |
| using AppFunc = Func<IDictionary<string, obj>, Task>; | |
| using MidFunc = Func<AppFunc, AppFunc>; | |
| public class MidFuncType | |
| { | |
| private readonly AppFunc next; | |
| public MidFuncType(AppFunc next) | |
| { | |
| this.next = next; | |
| } | |
| public Task Invoke(IDictionary<string, obj> environment) | |
| { | |
| // invoke the middleware, call `next`, etc. | |
| // return the `Task` | |
| return Task.FromResult<obj>(null) :> Task | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| open System | |
| open System.Collections.Generic | |
| open System.Threading.Tasks | |
| type AppFunc = Func<IDictionary<string, obj>, Task> | |
| type MidFunc = Func<AppFunc, AppFunc> | |
| type MidFuncType(next: AppFunc) = | |
| // set up the middleware | |
| member this.Invoke(environment: IDictionary<string, obj>) : Task = | |
| // invoke the middleware, call `next`, etc. | |
| // return the `Task` | |
| Task.FromResult<obj>(null) :> Task |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Threading.Tasks; | |
| using Microsoft.Owin; | |
| using System.Web.Http.Owin; | |
| using AppFunc = Func<IDictionary<string, obj>, Task> | |
| /// See https://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin/Infrastructure/AppFuncTransition.cs | |
| internal sealed class AppFuncTransition : OwinMiddleware | |
| : base(null) | |
| { | |
| private readonly AppFunc next; | |
| public AppFuncTransition(next: AppFunc) | |
| { | |
| this.next = next; | |
| } | |
| public Task Invoke(IOwinContext context) | |
| { | |
| // TODO: check for null | |
| return this.next(context.Environment) | |
| } | |
| } | |
| /// Explicit wrapper for HttpMessageHandlerAdapter | |
| public class OwinMessageHandlerMiddleware | |
| { | |
| private readonly OwinMiddleware next; | |
| OwinMessageHandlerMiddleware(AppFunc next, HttpMessageHandlerAdapterOptions /* I think this is the right name */ options) | |
| { | |
| var nextKatana = new AppFuncTransition(next); | |
| this.next = new HttpMessageHandlerAdapter(nextKatana, options); | |
| } | |
| public Task Invoke(IDictionary<string, obj> environment) | |
| { | |
| // TODO: check for null | |
| var context = new OwinContext(environment); | |
| return next(context); | |
| } | |
| } | |
| // This can be made generic: | |
| /// Generic OwinMiddleware adapter | |
| public abstract class OwinMiddlewareAdapter | |
| { | |
| private readonly OwinMiddleware next; | |
| protected OwinMiddlewareAdapter(AppFunc next, Func<OwinMiddleware, OwinMiddleware> factory) | |
| { | |
| var nextKatana = new AppFuncTransition(next); | |
| this.next = factory(nextKatana); | |
| } | |
| public Task Invoke(IDictionary<string, obj> environment) | |
| { | |
| // TODO: check for null | |
| var context = new OwinContext(environment) | |
| return next(context) | |
| } | |
| } | |
| /// HttpMessageHandlerAdapter using the Generic OwinMiddleware adapter | |
| public class OwinMessageHandlerMiddlewareAdapter : OwinMiddlewareAdapter | |
| { | |
| public OwinMessageHandlerMiddlewareAdapter(AppFunc next, HttpMessageHandlerAdapterOptions /* I think this is the right name */ options) | |
| : base(next, m => new HttpMessageHandlerAdapter(m, options)) | |
| { | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| open System | |
| open System.Collections.Generic | |
| open System.Threading.Tasks | |
| open Microsoft.Owin | |
| open System.Web.Http.Owin | |
| type AppFunc = Func<IDictionary<string, obj>, Task> | |
| /// See https://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin/Infrastructure/AppFuncTransition.cs | |
| [<Sealed>] | |
| type AppFuncTransition(next: AppFunc) = | |
| inherit OwinMiddleware(null) | |
| default x.Invoke(context: IOwinContext) = | |
| // TODO: check for null | |
| next.Invoke(context.Environment) | |
| /// Explicit wrapper for HttpMessageHandlerAdapter | |
| type OwinMessageHandlerMiddleware(next: AppFunc, options) = | |
| let nextKatana = AppFuncTransition(next) :> OwinMiddleware | |
| let webApiKatana = new HttpMessageHandlerAdapter(nextKatana, options) | |
| member x.Invoke(environment: IDictionary<string, obj>) = | |
| // TODO: check for null | |
| let context = new OwinContext(environment) | |
| webApiKatana.Invoke(context) | |
| // This can be made generic: | |
| /// Generic OwinMiddleware adapter | |
| type OwinMiddlewareAdapter(next: AppFunc, factory: Func<OwinMiddleware, OwinMiddleware>) = | |
| let nextKatana = AppFuncTransition(next) :> OwinMiddleware | |
| let middlewareKatana = factory.Invoke(nextKatana) | |
| member x.Invoke(environment: IDictionary<string, obj>) = | |
| // TODO: check for null | |
| let context = new OwinContext(environment) | |
| middlewareKatana.Invoke(context) | |
| /// HttpMessageHandlerAdapter using the Generic OwinMiddleware adapter | |
| type OwinMessageHandlerMiddlewareAdapter(next: AppFunc, options) = | |
| inherit OwinMiddlewareAdapter(next, (fun m -> new HttpMessageHandlerAdapter(m, options) :> _)) | |
I hope that helps someone else. If there’s interest, I can package this up and place it on NuGet, though it seems a lot of effort for something so small. The source code above is released under the Apache 2 license, in keeping with the use of AppFuncTransition used above from the Katana source.