Dusted Codes

Programming adventures

Functional ASP.NET Core

In December 2016 I participated in the F# Advent Calendar where I wrote a blog post on running Suave in ASP.NET Core. As part of that blog post I introduced the Suave.AspNetCore NuGet package which makes it possible to run a Suave web application inside ASP.NET Core via a new middleware.

So far this has been pretty good and as of last week the GitHub repository has been moved to the official SuaveIO GitHub organisation as well. A while ago someone even tweeted me that the performance has been pretty good too:

suave-aspnet-core-perf-tweet

Even though this made me very happy there was still one thing that bugged me until today.

Why I created Suave.AspNetCore

My main motivation for running Suave inside ASP.NET Core was to benefit from the speed and power of Kestrel while still being able to build a web application in a functional approach. Suave.AspNetCore made this possible, but afterwards I realised that this was not my final goal yet.

Ultimately I would like to build a web application in ASP.NET Core with a functional framework which does not only benefit from Kestrel but also from the entire ASP.NET Core eco system, including other middleware such as static files, authentication, authorization, security, the flexibility of the config system, logging or simply being able to retrieve information from the current hosting environment and more.

There is a lot of great features in ASP.NET Core which have been carefully crafted by experts (e.g. security) and I wouldn't want to miss out on those based on the framework of my choice. Unfortunately with Suave and Suave.AspNetCore I am limited in what other middleware I can use in combination with a Suave ASP.NET Core web application.

Suave and ASP.NET Core - Buddies but not family

Suave doesn't naturally fit into ASP.NET Core the way MVC does.

The reason is because Suave is not only a web framework but more of a web platform similar to what ASP.NET Core is itself. It's probably never been intended to be integrated with ASP.NET Core in the first place.

Think of it like this, ASP.NET Core is a web platform which sets the ground work for building any web application and MVC is a framework on top of the platform, which enables building web applications with an object oriented Model-View-Controller design pattern. Ideally as an F# developer I would like to replace the object oriented MVC framework with a functional equivalent, but keep the rest of ASP.NET Core's offering at the same time. This is very difficult with Suave at the moment.

Suave has its own HTTP server, its own Socket server and its own HTTP abstractions. As a result Suave's Socket implementation is not compatible with ASP.NET Core's web socket server and Suave's HttpContext is vastly different from ASP.NET Core's HttpContext.

This is why the Suave.AspNetCore middleware has to translate one HttpContext into another and then back again. It works, but it is not ideal, because there's a lot of information that gets lost along the way (e.g. the User object in ASP.NET Core is of type IPrincipal and in Suave it is a Map<string, obj>). This is a limiting factor. For example there's no way to access the Authentication property, the Session object or the Features collection of the original ASP.NET Core HttpContext from inside a Suave application.

This means that even though I can run a Suave web application in ASP.NET Core, I still have to re-build a lot of the ground work that has been laid out for me by other ASP.NET Core middleware. In some cases this might be a minor problem, but in others I consider it a big issue. Especially when it comes to critical components such as security I would much rather want to rely on the implementation provided by Microsoft and other industry experts than (re-)inventing it myself.

None of this is Suave's fault though, because it has not been designed with ASP.NET Core in mind, which is more than fair, but for people like me who want to benefit from both platforms this is still an important issue to consider.

Why ASP.NET Core?

If Suave is already a well working standalone product for building web applications in a functional way, why would I even bother with ASP.NET Core? Well this is a good question and I can only answer it for myself.

For me the main reasons for using ASP.NET Core are the following:

Performance

Kestrel is extremely fast and the Microsoft team is working hard on making it even faster. Depending on what type of application you are trying to build this can be a big or a small selling point.

Personally I am working on a project which anticipates a significant load at most times and therefore performance is key. I also have a few smaller side projects which run in Docker containers in the cloud and the quicker these applications can handle web requests, the less I have to pay for additional computation power.

Security

As mentioned before I am very reluctant to using 3rd party security code which hasn't been vetted, audited and tested to the same depth as the one provided by Microsoft and the ASP.NET Core team. They have years of invaluable experience and I trust them with security more than other individuals who I barely know or even myself. Call me pessimistic, but security is such a complex topic that I don't even think that a single person should do this on their own. It's one of those things where I believe you have to have a big team of experts and resources behind you to stand a chance against the various vulnerabilities of today.

Laziness

I am a lazy developer. I don't enjoy building the 100th logging framework or coming up with yet another configuration API. I want to build new original ideas and not waste my time on stuff which has been done by thousands of other developers before me. ASP.NET Core is a platform which offers many things out of the box and essentially saves me a lot of valuable time.

Additionally it offers easy integration points for other 3rd party code and has a huge developer base behind it which gives me access to even more useful tools and libraries which can benefit my projects.

Community

The community around ASP.NET (Core) is probably the biggest of all .NET web frameworks. There is significantly more people reporting issues, working on bug fixes and building new features into the product than anyone else. The value of such a vibrant community shall not be underestimated. There's nothing better than having bugs fixed by other people before I even encounter them myself.

Another great side effect is that a lot of my questions might have already been answered on StackOverflow and that there is a ton of other useful blog posts explaining stuff that I don't have to work out myself. As an employer it might be also beneficial to pick a framework which has a bigger talent pool than others.

Impatience

I am very impatient and for me it is very important that certain issues get addressed fairly quickly. For instance in recent years many web servers were upgrading to support HTTP/2 which is an important improvement over HTTP/1.1. My chances of getting such critical updates are probably higher with a product which is used by thousands of developers than something else which is maybe a little bit more niche. This is not always true, but from my experience this is generally the case.

Fear of missing out

We don't know what the future holds for us. Tomorrow someone might release something incredibly awesome which might not be available on smaller platforms in its initial phase. I love working with bleeding edge technology and as such I do have a certain degree of FOMO when it comes to software innovations.

Support

Lastly ASP.NET Core, even though open source, remains an enterprise supported product and that has a lot of value as well. Over the years I had to replace many successful open source packages because the original maintainers got tired of supporting it and no one else stepped in to replace them, which meant they became stale and essentially completely out of date. There is no guarantee that Microsoft will support the ASP.NET (Core) platform for ever, but from the looks of it I don't expect it to go away any time soon either. In fact the current signs seem to suggest the exact opposite considering that ASP.NET itself has already more than a decade on its shoulders and Microsoft recently decided to invest in a complete new re-design to continue its success.

Not everyone might agree with my reasoning, but for me these are very compelling points to stick with ASP.NET Core as my preferred .NET web platform and try to use as much of its ready made features as possible.

Building a functional framework for ASP.NET Core

Now that I have explained why I would like to build a web application with ASP.NET Core I have the only problem that as an F# developer there's no ideal framework available yet.

This got me thinking what if I could build my own little micro framework in F# that borrows the functional design pattern of Suave, but embraces the power of ASP.NET Core?

Defining a functional HttpHandler

A functional ASP.NET Core web application could look as simple as a function which takes in a HttpContext and returns a HttpContext. In functional programming everything is a function after all. Inside that function it would have full access to the Request and Response object as well as all the other objects to successfully process an incoming web request and return a response.

I could call such a web function a HttpHandler:

type HttpHandler = HttpContext -> HttpContext

Not every incoming web request can or should be handled by a HttpHandler. For example if the request was made to a route which wasn't anticipated, like a static file which should be picked up by the static file middleware, then a HttpHandler should be able to skip this particular request.

In this case there should be an option to return nothing so that another HttpHandler can try to satisfy the incoming request or the calling middleware can defer the HttpContext to the next middleware:

type HttpHandler = HttpContext -> HttpContext option

By making the HttpContext optional the function can now either return Some HttpContext or None.

Additionally the HttpHandler shouldn't block on IO operations or other long running tasks and therefore return the HttpContext option wrapped in an asynchronous workflow:

type HttpHandler = HttpContext -> Async<HttpContext option>

This is slowly taking shape, but there's still something missing.

In ASP.NET Core MVC controller dependencies are automatically resolved during instantiation. This is very useful, because in ASP.NET Core dependencies are registered in a central place inside the ConfigureServices method of the Startup.cs class file.

Automatic dependency resolution is not really a thing in functional programming, because dependencies are normally functions and not objects. Functions can be passed around or partially applied which usually makes object oriented dependency management obsolete.

However, because most ASP.NET Core dependencies are registered as objects inside an IServiceCollection container, a HttpHandler can resolve these dependencies through an IServiceProvider object.

This is done by wrapping the original HttpContext and an IServiceProvider object inside a new type called HttpHandlerContext:

type HttpHandlerContext =
    {
        HttpContext : HttpContext
        Services    : IServiceProvider
    }

By changing the HttpHandler function definition we can take advantage of this new type:

type HttpHandler = HttpHandlerContext -> Async<HttpHandlerContext option>

With that one should be able to build pretty much any web application of desire. If you have worked with Suave in the past then this should look extremely familiar as well.

Combining smaller HttpHandlers to bigger applications

In principal there's nothing you cannot do with an HttpHandler, but it wouldn't be very practical to build a whole web application in one function. The beauty of functional programming is the composition of many smaller functions to one bigger application.

The simplest combinator would be a bind function which takes two HttpHandler functions and combines them to one:

let bind (handler : HttpHandler) (handler2 : HttpHandler) =
    fun (ctx : HttpHandlerContext) ->
        async {
            let! result = handler ctx
            match result with
            | None      -> return None
            | Some ctx2 -> return Some ctx2
        }

As you can see the bind function takes two different HttpHandler functions and a HttpHandlerContext. First it evaluates the first handler and checks its result. If the result was None then it will stop at this point and return None as the final result. If the result was Some HttpHandlerContext then it will take the resulting context and use it to evaluate the second HttpHandler. Whatever the second HttpHandler returns will be the final result in this case.

This pattern is often referred to as railway oriented programming. If you are interested to learn more about it then please check out Scott Wlaschin's slides and video on his website or this lengthy blog post on that topic.

One more thing that should be considered in the bind function is to check if a HttpResponse has been already written by the first HttpHandler before invoking the second HttpHandler. This is required to prevent a potential exception when a HttpHandler tries to make changes to the HttpResponse after another HttpHandler has already written to the response. In this case the bind function should not invoke the second HttpHandler:

let bind (handler : HttpHandler) (handler2 : HttpHandler) =
    fun (ctx : HttpHandlerContext) ->
        async {
            let! result = handler ctx
            match result with
            | None      -> return None
            | Some ctx2 ->
                match ctx2.HttpContext.Response.HasStarted with
                | true  -> return  Some ctx2
                | false -> return! handler2 ctx2
        }

To round this up we can bind the bind function to the >>= operator:

let (>>=) = bind

With the bind function we can combine unlimited HttpHandler functions to one.

The flow would look something like this:

aspnet-core-lambda-http-handler-flow-cropped

Another very useful combinator which can be borrowed from Suave is the choose function. The choose function let's you define a list of multiple HttpHandler functions which will be iterated one by one until the first HttpHandler returns Some HttpHandlerContext:

let rec choose (handlers : HttpHandler list) =
    fun (ctx : HttpHandlerContext) ->
        async {
            match handlers with
            | []                -> return None
            | handler :: tail   ->
                let! result = handler ctx
                match result with
                | Some c    -> return Some c
                | None      -> return! choose tail ctx
        }

In order to better see the usefulness of this combinator it's best to look at an actual example.

Let's define a few simple HttpHandler functions first:

let httpVerb (verb : string) =
    fun (ctx : HttpHandlerContext) ->
        if ctx.HttpContext.Request.Method.Equals verb
        then Some ctx
        else None
        |> async.Return

let GET     = httpVerb "GET"    : HttpHandler
let POST    = httpVerb "POST"   : HttpHandler

let route (path : string) =
    fun (ctx : HttpHandlerContext) ->
        if ctx.HttpContext.Request.Path.ToString().Equals path 
        then Some ctx
        else None
        |> async.Return

let setBody (bytes : byte array) =
    fun (ctx : HttpHandlerContext) ->
        async {            
            ctx.HttpContext.Response.Headers.["Content-Length"] <- new StringValues(bytes.Length.ToString())
            ctx.HttpContext.Response.Body.WriteAsync(bytes, 0, bytes.Length)
            |> Async.AwaitTask
            |> ignore
            return Some ctx
        }
		
let setBodyAsString (str : string) =
    Encoding.UTF8.GetBytes str
    |> setBody

This already gives a good illustration of how easily one can create different HttpHandler functions to do various things. For instance the httpVerb handler checks if the incoming request matches a given HTTP verb. If yes it will proceed with the next HttpHandler, otherwise it will return None. The two functions GET and POST re-purpose httpVerb to specifically check for a GET or POST request.

The route function compares the request path with a given string and either proceeds or returns None again. Both, setBody and setBodyAsString write a given payload to the response of the HttpContext. This will trigger a response being made back to the client.

Each HttpHandler is kept very short and has a single responsibility. Through the bind and choose combinators we can combine many HttpHandler functions into one larger web application:

let webApp =
    choose [
        GET >>=
            choose [
                route "/"     >>= setBodyAsString "Index"
                route "/ping" >>= setBodyAsString "pong"
            ]
        POST >>=
            choose [
                route "/submit" >>= setBodyAsString "Submitted!"
                route "/upload" >>= setBodyAsString "Uploaded!"
            ]
    ]

Even though I've barely written any code this functional framework already proves to be quite powerful.

At last I need to create a new middleware which can run this functional web application:

type HttpHandlerMiddleware (next     : RequestDelegate,
                            handler  : HttpHandler,
                            services : IServiceProvider) =
    
    do if isNull next then raise (ArgumentNullException("next"))

    member __.Invoke (ctx : HttpContext) =
        async {
            let httpHandlerContext =
                {
                    HttpContext = ctx
                    Services    = services
                }
            let! result = handler httpHandlerContext
            if (result.IsNone) then
                return!
                    next.Invoke ctx
                    |> Async.AwaitTask
        } |> Async.StartAsTask

And finally hook it up in Startup.cs:

type Startup() =
    member __.Configure (app : IApplicationBuilder) =
        app.UseMiddleware<LambdaMiddleware>(webApp) |> ignore

This web framework shows what I love about F# so much. With very little code I was able quickly write a basic web application from the ground up. It looks very much like an ASP.NET Core clone of Suave, but with the difference that it fully embraces the ASP.NET Core architecture and its HttpContext.

Functional ASP.NET Core framework

By extending the above example with a few more useful HttpHandler functions to return JSON, XML, HTML or even templated (DotLiquid) views someone could create a very powerful ASP.NET Core functional web framework, which could be easily seen as an MVC replacement for F# developers.

This is exactly what I did and I named it ASP.NET Core Lambda, because I simply couldn't think of a more descriptive or "cooler" name. It is a functional ASP.NET Core micro framework and I've built it primarily for my own use. It is still very early days and in alpha testing, but I already use it for two of my private projects and it works like a charm.

How does it compare to other .NET web frameworks

Now you might ask yourself how does this compare to other .NET web frameworks and particularly to Suave (since I've borrowed a lot of ideas from Suave and from Scott Wlaschin's blog)?

I think this table explains it very well:

Paradigm Language Hosting Frameworks
MVC Object oriented C# ASP.NET (Core) only Full .NET, .NET Core
NancyFx Object oriented C# Self-hosted or ASP.NET (Core) Full .NET, .NET Core, Mono
Suave Functional F# Primarily self-hosted Full .NET, .NET Core, Mono
Lambda Functional F# ASP.NET Core only .NET Core

MVC and NancyFx are both heavily object-oriented frameworks mainly targeting a C# audience. MVC probably the most feature rich framework and NancyFx with its super-duper-happy-path are probably the most wide spread .NET web frameworks. NancyFx is also very popular with .NET developers who want to run a .NET web application self-hosted on Linux (this was a big selling point pre .NET Core times).

Suave was the first functional web framework built for F# developers. It is a completely independent standalone product which was designed to be cross platform compatible (via Mono) and self-hosted. A large part of the Suave library is compounded of its own HTTP server and Socket server implementation. Unlike NancyFx it is primarily meant to be self-hosted, which is why the separation between web server and web framework is not as clean cut as in NancyFx (e.g. the Suave NuGet library contains everything in one while NancyFx has separate packages for different hosting options).

ASP.NET Core Lambda is the smallest of all frameworks (and is meant to stay this way). It is also a functional web framework built for F# developers, but cannot exist outside of the ASP.NET Core platform. It has been tightly built around ASP.NET Core to leverage its features as much as possible. As a result it is currently the only (native) functional web framework which is a first class citizen in ASP.NET Core.

I think it has its own little niche market where it doesn't really compete with any of the other web frameworks. It is basically aimed at F# developers who want to use a ASP.NET Core in a functional way.

While all these web frameworks share some similarities they still have their own appliances and target a different set of developers.

Watch this space for more updates on ASP.NET Core Lambda in the future and feel free to try it out and let me know how it goes! So far I've been very happy with the results and it has become my goto framework for new web development projects.

Comments

Thank you Microsoft for being awesome

OK so I have to admit that I can have a pretty big mouth sometimes, which is certainly not a good quality to have. I don't shy away to be (overly) critical if things annoy me:

tweet-0

As you can see, about a year ago I was a little bit upset that a great new product received a not so great new name. I really hoped that ASP.NET Core would have been named something much "cooler" for my taste.

So my frustration continued...

tweet-1

(Let's be honest, it is probably a good thing that I don't have many Twitter followers.)

And even a year later I feel like I am still suffering sometimes:

tweet-2

But I don't care about the name anymore.

Because the product is actually really good.

Yes, the name is still an issue when you google for stuff, and yes, the tooling is still in preview, and yes, the support for F# is not even in RC yet, and yes, there's still lots of other little annoyances which would be nice to have fixed soon, but when I look past all that for a moment then I have to admit that the product is actually really freaking nice.

Being harsh is easy

Often it is very easy to be very quickly critical, but much harder to acknowledge someone's good work. It's already an effort to think about praising people who did a great job at work, let alone praising some third party who you are not even directly related to.

But I think as someone who depends a lot on these third parties, corporations like Microsoft, who own a lot of the tech which I love and use every day, and other contributors from the open source community, then it's important to give back to these people as well.

Particularly with companies like Microsoft many people feel entitled to be overly rude or critical if things don't work out immediately the way they want it to be (and not just Microsoft but really any company of similar size). It is so easy to forget good manners when one is thinking of a big company logo rather than actual human beings, because it is much easier to throw dirt at a logo than a person.

Microsoft_logo

But behind these logos there is still human beings working hard on the products which we use every day and they are not any different from us. Women and men who got into software development and IT for the same good reasons as we did, who share the same passion as we do and who make the same mistakes as us, even when they have only the best in their intention.

Microsoft has made a lot of mistakes in the past (Vista, Windows 8, Silverlight, Windows Phone, Skype, you name it!) and we've not come short to let them know how much these products sucked, but in most recent years they've actually done an amazing turnaround and even taken the lead in some of the most exciting innovations of our field and I am certainly not the only one who has noticed it.

Awesome things by Microsoft

Here's a list of a some of the many awesome things that happened at Microsoft in recent years (in no particular order):

This is a lot of (radical) positive change in a comparatively short amount of time when you think about the size of the company!

For example, check out the Surface Studio introduction video which demonstrates 3D drawing on a Surface Studio:

surface-studio-3d-drawing

You have to admit that this is awesome. I mean I am not even a designer and I get a watery mouth by looking at it.

To be honest the whole Microsoft Surface product series is awesome and I speak from personal experience. Last Black Friday I bought my partner a new Surface Pro 4 (I accidentally spilled a drink over her old laptop) and boy was I impressed with her new device. It's slick, it's fast and when I tried out the drawing with the pencil then my jaw dropped and I was left with pure jealousy (and I say that even though I have a Lenovo Yoga 900 which is a pretty neat device itself).

But that is not even why I am so pleased with Microsoft in recent days. As a .NET developer I am mostly impressed by the work the teams have put into .NET Core, ASP.NET Core, C#, F# and the tooling around it.

.NET Core, ASP.NET Core, F# and Open Source

Especially the ASP.NET Core team led by Scott Hanselman and Damian Edwards is an excellent example of how well Microsoft functions today. Don't get me wrong, I know that ASP.NET Core is still far away from being perfect and that there's still loads of stuff that needs to be done before everyone can use it, but I am still amazed by how far this team has come today.

They have taken a 10+ year old technology, completely revamped it and transitioned from an entirely closed and proprietary product team to one of the most approachable open source contributors that I have ever seen. Not only is their code on GitHub, but they are putting an immense amount of time and effort into keeping a close relationship to the .NET community. They are active on GitHub, Slack, Gitter, Twitter and heck they even live stream a weekly standup where they demo the latest stuff hot from the press, answer live questions to the community, give updates on the current development status and talk about the roadmap. They even spend time going through blog posts written by non-Microsofties where Jon Galloway presents a whole list of community written content at the beginning of each standup. This has become a nice ritual where the Microsoft team gives back to other open source contributors and help nourish a friendly and healthy community.

I find this is amazing work. I don't know many other teams in the world who put this much effort into their open source work.

This is not Microsoft trying to put some code on GitHub and then turning on the #OpenSource marketing machinery. No, this is Microsoft showing the rest of the world how open source is done right (and I am not trying to undermine the open source work by others here, just trying to highlight how well it's done at MSFT).

Also I'd like to point out that this team is doing the difficult balancing act of developing a completely new technology which is supposed to entertain an already existing (and very spoiled) community as well as trying to attract new programmers from the open source field. I guess this is much harder than just launching a new product into the market like Go, Rust or Node.js which didn't have an existing developer base to please.

FSharp

I have been a C# developer for almost the entirety of my professional career and only in the last year I really fell in love with functional programming and F#. For me F# is probably one of the most powerful languages on the market right now. It has the horse power of .NET under the hood with the beauty of a modern functional language on the surface and it has been open source, cross platform compatible and community driven from the very beginning of its time.

So with all of that I've got something to say which was overdue for a while now:

Thank you Microsoft for being awesome!

Seriously, a special thanks to all of the Microsoft teams who work on .NET Core, ASP.NET Core, Visual Studio, Visual Studio Code, C#, F# and the entire goodness around it!!! Your products are awesome and because of your great work I enjoy working in the .NET eco-system more than ever before!

This is not me being sarcastic, I am dead serious, so thank you guys!

I hope when you read this then you know that despite all the negativity that Microsoft has to suck up every now and then that there's also a lot of developers who think you are doing an amazing job and that without you we would be probably some poor suckers who would have to work with Node.js or some other soul destroying technology!

Join me in saying thank you

If you are a .NET developer who loves working with C# or F#, who thinks that Visual Studio (Code) is one of the best IDEs, who thinks that ASP.NET Core and .NET Core are awesome new development stacks then please join me in saying thank you to these guys so that they know that all of their hard work is paying off.

Oh and before someone labels me a Microsoft fanboy or thinks that there's a hidden agenda behind this blog post then let me tell you this:

Sometimes is just feels damn good to be nice.

...something which I would wish programmers would do much more to each other, particularly in such divisive times like today.

Comments

Older Posts Newer Posts