Dusted Codes

Programming adventures

Functional ASP.NET Core part 2 - Hello world from Giraffe

This is a follow up blog post on the functional ASP.NET Core article from about two months ago. First of all I'd like to say that this has been the longest period I haven't published anything new to my blog since I started blogging in early 2015. The reason is because I have been pretty busy with a private project which I hope to write more about in the future, but more importantly I have been extremely busy organising my own wedding which took place at the end of last month :). Yes!, I've been extremely lucky to have found the love of my life and best friend and after being engaged for almost a year and a half we've finally tied the knot two weeks ago. Normally I don't blog about my private life here, but since this has been such a significant moment in my life I thought I should mention a few words here as well and let everyone know that the quiet time has been for a good reason and will not last for much longer now.

While this has primarily occupied the majority of my time I was also quite happy to see my functional ASP.NET Core project receive recognition from the community and some really great support from other developers who've been helping me in adding lots of new functionality since then. In this first blog post after my small break I thought I'd take the opportunity and showcase some of the work we've done since the initial release and explain some of the design decisions behind some features.

But first I shall say that the framework has been renamed to Giraffe.

ASP.NET Core Lambda is now Giraffe

ASP.NET Core Lambda was a good name in terms of being very descriptive of what it stood for, but at the same time there were plenty of other issues which led me and other people believe that a different name would be a better fit.

Initially I named the project ASP.NET Core Lambda, because, at its core it was a functional framework built on top of (and tightly integrated with) ASP.NET Core, so I put one and one together and went with that name.

However, it quickly became apparent that "ASP.NET Core Lambda" wasn't a great name for the following reasons:

As a result I decided to rename the project to something different and put the name up for a vote, which ultimately led to Giraffe. Looking back I think it was a great choice and I would like to thank everyone who helped me in picking the new name, as well as suggesting other great names which made the decision not easy at all.

I think Giraffe is a much better name now, because it is short, it is very clear and distinctive and there is no ambiguity around the spelling or pronunciation. There is also no other product called Giraffe in the .NET space and not really anything else which it could be mistaken with. The name Giraffe also hasn't been taken as a NuGet package which made things really easy. On top of that Giraffe gave lots of creative room for creating a beautiful logo for which I used 99designs.co.uk. I set up a design challenge there and the winner impressed with this clever design:

Giraffe Logo

Now I can only hope that the product will live up to this beautiful logo and the new name, which brings me to the actual topic of this blog post.

Overview of new features

There has been quite a few changes and new features since my last blog post and there's a few of which I am very excited about:

Dotnet new template

One really cool thing you can do with the new .NET tooling is to create project templates which can be installed via NuGet packages.

Thanks to David Sinclair you can install a Giraffe template by running the following command:

dotnet new -i giraffe-template::*

This will install the giraffe-template NuGet package to your local templates folder.

Afterwards you can start using Giraffe as a new project type when running the dotnet new command:

dotnet new giraffe

This feature makes it significantly easier to get started with Giraffe now. The quickest way to get a working Giraffe application up and running is by executing these three commands:

  1. dotnet new giraffe
  2. dotnet restore
  3. dotnet run

Everything should compile successfully and you should see a Hello-World Giraffe app running behind http://localhost:5000.

Nested routing

Another cool feature which has been added by Stuart Lang is nested routing.

The new subRoute handler allows users to create nested routes which can be very useful when logically grouping certain paths.

An example would be when an API changes it's authentication scheme and you'd want to group routes together which implement the same type of authentication. With the help of nested routing you can enable certain features like a new authentication scheme by only declaring it once per group:

let app = 
    subRoute "/api"
        (choose [
            subRoute "/v1"
                (oldAuthentication >=> choose [
                    route "/foo" >=> text "Foo 1"
                    route "/bar" >=> text "Bar 1" ])
            subRoute "/v2"
                (newAuthentication >=> choose [
                    route "/foo" >=> text "Foo 2"
                    route "/bar" >=> text "Bar 2" ]) ])

In this example a request to http://localhost:5000/api/v1/foo will use oldAuthentication and a request to http://localhost:5000/api/v2/foo will end up using newAuthentication.

There is also a subRouteCi handler which is the case insensitive equivalent of subRoute.

Razor views

Next is the support of Razor views in Giraffe. Nicolás Herrera developed the first version of Razor views by utilising the RazorLight engine. Shortly after that I realised that by referencing the Microsoft.AspNetCore.Mvc NuGet package I can easily re-use the original Razor engine in order to offer a more complete and original Razor experience in Giraffe as well. While under the hood the engine changed from RazorLight to ASP.NET Core MVC the functionality remained more or less the same as implemented by Nicolás in the first place.

In order to enable Razor views in Giraffe you have to register it's dependencies first:

type Startup() =
    member __.ConfigureServices (svc : IServiceCollection,
                                 env : IHostingEnvironment) =    
        Path.Combine(env.ContentRootPath, "views")
        |> svc.AddRazorEngine
        |> ignore

After that you can use the razorView handler to return a Razor page from Giraffe:

let model = { WelcomeText = "Hello World" }

let app = 
    choose [
        route "/" >=> razorView "text/html" "Index" model
    ]

The above example assumes that there is a /views folder in the project which contains an Index.cshtml file.

One of the parameters passed into the razorView handler is the mime type which should be returned by the handler. In this example it is set to text/html, but if the Razor page would represent something different (like an SVG image template for example) then with the razorView handler you can also set a different Content-Type as well.

In most cases text/html is probably the desired Content-Type of your response and therefore there is a second handler called razorHtmlView which does exactly that:

let model = { WelcomeText = "Hello World" }

let app = 
    choose [
        route  "/" >=> razorHtmlView "Index" model
    ]

A more involved example with a layout page and a partial view can be found in the SampleApp project in the Giraffe repository.

Using DotNet Watcher to reload the project on Razor page changes

If you come from an ASP.NET Core MVC background then you might be used to having Razor pages automatically re-compile on every page change during development, without having to manually restart an application. In Giraffe you can achieve the same experience by adding the DotNet.Watcher.Tools to your .fsproj and put a watch on all .cshtml files:

<ItemGroup>
    <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0" />
</ItemGroup>
  
<ItemGroup>
    <Watch Include="**\*.cshtml" Exclude="bin\**\*" />
</ItemGroup>

By adding the watcher to your project file you can start making changes to any .cshtml file in your project and immediately see the changes take effect during a running Giraffe web application (without having to manually restart the app).

Dependency on Microsoft.AspNetCore.Mvc

One other thing which might sound a little bit strange is the dependency on the Microsoft.AspNetCore.Mvc NuGet package. It is essentially the full MVC library being referenced by Giraffe now and it has sparked a bit of confusion or disappointment amongst some users. Personally I think it really doesn't matter and I wanted to explain my thinking behind this design decision.

In order to get Razor views working in Giraffe there were three options available:

I certainly did not have an appetite for the first option, which is hopefully understandable, and therefore was left with the choice between the latter two.

At the time of writing there was only one .NET Core compatible custom Razor engine available, which is RazorLight. From what I know RazorLight is a very nice library and definitely highly recommended, but not necessarily the right choice for Giraffe.

When you ignore the name of the NuGet package for a second then there is really not much difference between referencing RazorLight or Microsoft.AspNetCore.Mvc in Giraffe. Both require a new NuGet dependency in the project and both are a library which exposes some functionality to render Razor views. The ASP.NET Core MVC package might be slightly bigger and offer more functionality than what Giraffe actually needs, but that doesn't really matter, because Giraffe ignores the rest and only uses what is needed for the Razor support. I think it is pretty normal that any given library often implements far more functionality than what a single project actually makes use of.

In the case of Giraffe I was faced with a trade-off between a dependency which uses slightly more KBs disk space, but in return offers a complete and original Razor experience vs. a slightly smaller library which offers a custom implementation of Razor pages.

As far as I see this issue there is absolutely no disadvantage in Giraffe using the MVC NuGet package in order to get the original Razor experience in comparison to using any other Razor library. I also believe that this option is more in line with Giraffe's goal to be tightly integrated with the original ASP.NET Core experience. Users benefit by getting the original, well documented and understood Razor features which makes portability of existing Razor views also significantly easier.

For me it's really about making smart choices and I truly believe that the strength of Giraffe is by standing on the shoulders of giants, which is ASP.NET Core MVC in this case.

Functional HTML view engine

Speaking of the Razor view engine, another really cool feature which has been added to Giraffe is a new programmatic way of creating views. Florian Verdonck helped me a lot with Giraffe over the last few weeks and one of his contributions was to port Suave's experimental Html engine to Giraffe.

I think the best way to describe the new Giraffe.HtmlEngine is by showing some code:

open Giraffe.HtmlEngine

let model = { Name = "John Doe" }

let layout (content: HtmlNode list) =
    html [] [
        head [] [
            title [] (encodedText "Giraffe")
        ]
        body [] content
    ]

let partial () =
    p [] (encodedText "Some partial text.")

let personView model =
    [
        div [] [
                h3 [] (sprintf "Hello, %s" model.Name |> encodedText)
            ]
        div [] [partial()]
    ] |> layout

let app = 
    choose [
        route "/" >=> (personView model |> renderHtml)
    ]

This examples demonstrates well how easy it is to create complex views with features like layout pages, partial views and model binding. There's really nothing that can't be done with this programmatic way of defining view-model pages and if you think there's something missing then it is really easy to extend as well.

Kudos to the Suave guys for coming up with this brilliant view engine and thanks to Florian for suggesting this feature as well as liaising with the Suave guys and porting the code to Giraffe!

Content negotiation

The next feature which I'd like to show off is something which I did myself for a change. When exposing a web service endpoint you often want to respect the client's requested response type which is typically communicated via the HTTP Accept header.

For example a client might send the following information with a HTTP Accept header:

Accept: application/xml,application/json,text/html;q=0.8,text/plain;q=0.9,*/*;q=0.5

In this example the client says the following:

In Giraffe you can use the negotiate and negotiateWith handlers to return the client the best matching response based on the information passed through the request's Accept header.

The negotiate handler is very simple and speaks JSON, XML and plain text at the moment:

[<CLIMutable>]
type Person =
    {
        FirstName : string
        LastName  : string
    }
    override this.ToString() =
        sprintf "%s %s" this.FirstName this.LastNam

let app = 
    choose [
        route  "/foo" >=> negotiate { FirstName = "Foo"; LastName = "Bar" }
    ]

By default the negotiate handler will check the request's Accept header and automatically serialize the model in either JSON, XML or plain text. If the client asks for plain/text then the negotiate handler will use the model's ToString() method otherwise it will use a JSON or XML serializer. Other mime types like text/html are not supported out of the box, because there is no default way to serialize an object into HTML.

However, if you want to support a wider range of accepted mime types then you can use the negotiateWith handler to set custom negotiation rules.

Let's assume you want to support two additional mime types, application/x-protobuf for Google's Protocol Buffers serialization and application/octet-stream for generic binary serialization.

First you would want to implement two new HttpHandler functions which can return a response of those exact types:

let serializeProtobuf x =
    // Implement protobuf serialization

let serializeBinary x =
    // Implement binary serialization

let protobuf (dataObj : obj) =
    setHttpHeader "Content-Type" "application/x-protobuf"
    >=> setBodyAsString (serializeProtobuf dataObj)

let binary (dataObj : obj) =
    setHttpHeader "Content-Type" "application/octet-stream"
    >=> setBodyAsString (serializeBinary dataObj)

Then you can use the two new HttpHandler functions to set up custom negotiation rules and use them with the negotiateWith handler:

let rules =
    dict [
        "*/*"                     , json
        "application/json"        , json
        "application/xml"         , xml
        "application/x-protobuf"  , protobuf
        "application/octet-stream", binary
    ]

let model = { FirstName = "Foo"; LastName = "Bar" }

let app = 
    choose [
        route  "/foo" >=> negotiateWith rules model
    ]

You might find it more convenient to create a new negotiate handler altogether, which will make it much less verbose to use the custom rules in subsequent routes:

let negotiate2 = negotiateWith rules

let app = 
    choose [
        route  "/foo" >=> negotiate2 { FirstName = "Foo"; LastName = "Bar" }
    ]

Even though there's still loads of room for improvement, I think this might be just enough for a large quantity of web applications already.

Model Binding

While the Accept HTTP header denotes what mime types a client understands (typically more than just one), the Content-Type HTTP header specifies which mime type a client/server has chosen to send a message with. This is very useful information when it comes to model binding.

Giraffe exposes five different model binding functions which can deserialize the content of a HTTP request into a strongly typed object. Four of them can bind a specific request type into a typed model and the fifth method picks the most appropriate model binding function based on the request's Content-Type header.

It's the easiest to demonstrate this with a quick example again. Let's assume we have the following record type in a web application:

[<CLIMutable>]
type Car =
    {
        Name   : string
        Make   : string
        Wheels : int
        Built  : DateTime
    }

Now I'd like to expose different endpoints which can be used to HTTP POST a car object to the web service:

open Giraffe.HttpHandlers
open Giraffe.HttpContextExtensions

let submitAsJson =
    fun (ctx : HttpContext) ->
        async {
            let! car = ctx.BindJson<Car>()			
            // Do stuff
        }
		
let submitAsXml =
    fun (ctx : HttpContext) ->
        async {
            let! car = ctx.BindXml<Car>()			
            // Do stuff
        }

let submitAsForm =
    fun (ctx : HttpContext) ->
        async {
            let! car = ctx.BindForm<Car>()			
            // Do stuff
        }
		
let submitAsQueryString =
    fun (ctx : HttpContext) ->
        async {
            let! car = ctx.BindQueryString<Car>()			
            // Do stuff
        }
	
let submitHowYouLike =
    fun (ctx : HttpContext) ->
        async {
            let! car = ctx.BindModel<Car>()			
            // Do stuff
        }
	
let webApp =
    POST >=>
        choose [
            route "/json"  >=> submitAsJson
            route "/xml"   >=> submitAsXml
            route "/form"  >=> submitAsForm
            route "/query" >=> submitAsQueryString
            route "/any"   >=> submitHowYouLike ]

As you can see from the example, the model binding functions are extension methods on the HttpContext object and require to open the Giraffe.HttpContextExtensions module.

The ctx.BindJson<'T>() function will always try to retrieve an object by deserializing data from JSON. The ctx.BindXml<'T>() function behaves the same way but will try to deserialize from XML. The ctx.BindForm<'T>() function will bind a model from a request which has a Content-Type of application/x-www-form-urlencoded (typically a POST request from a HTML form element).

Sometimes you might want to bind a model from a query string, which could not only come from a HTTP POST but also from any other HTTP verb request. In this instance the ctx.BindQueryString<'T>() function can be used to bind the values from a query string to a strongly typed model.

At last you might want to allow a client to submit an object via any of the above mentioned options on the same endpoint. In this case your endpoint has to pick the correct model binding based on the Content-Type HTTP header and this can be achieved with the ctx.BindModel<'T>() function.

Since all model binding functions are extension methods of the HttpContext type they can be used from anywhere in a web application where you have access to the HttpContext object, which in Giraffe's case is every single HttpHandler function.

What's next?

There were quite a few breaking changes since the first release, but APIs are slowly maturing as I get more feedback and exposure of the framework. So far the library has been in an alpha stage and will probably remain for another few weeks before I get around to finish some more examples and test projects which will eventually lead to the beta phase.

Once the project is in beta I will try to focus my effort more on collecting a lot of additional feedback before I feel confident enough to declare the first RC and subsequently the official version 1.0.0.

Even though breaking changes are not always the end of the world I would like to avoid drastic fundamental changes (as seen recently) once the project has entered the first stable release. Therefore I have been fairly reluctant to prematurely label Giraffe beyond an alpha and will probably want to enjoy the freedom of breaking stuff for a tiny bit longer. At the end of the day it's about setting the right expectations and I don't help anyone by labeling v1.0.0 too early when I know there's still a fair bit of danger to potentially move stuff around.

However, having said that I do want to stress that the underlying system (ASP.NET Core and Kestrel) have been very stable for a while now and as long as you don't mind that a namespace or method might still change in the near future then Giraffe is absolutely fit for production. So please go ahead and give it a try if you like what you've seen in this blog post so far :).

This basically brings me to the end of this follow up article and I thought what better way to finish it off than by sharing some of our memories from our wonderful wedding (in case you ever wondered what a British-Indian/Austrian-Polish wedding looks like ;)).

It was a very long day, which started off with a civil ceremony in the morning...

Civil ceremony in the morning of the day

Followed by a traditional Hindu ceremony shortly after lunch...

Drums during groom entrance at Hindu ceremony

Groom entrance at Hindu ceremony

Prayers at Hindu ceremony

Listening to Hindu priest cracking jokes

I had no idea how much fun Hindu ceremonies can be! There's lots of really fun and merry traditions which take place as part of us getting married. Then there's also a bit of banter between the two families. One of those little traditions is that the bride's family has to steal the groom's shoes before the ceremony ends so that the groom can't leave the house and take his newly wedded wife away from her family - at least not without having to pay for getting his shoes back. Normally this results in a bit of shoe pulling between the bride's side and the groomsmen, but I think in our case it is fair to say that there was a bit of a cultural clash when someone from my family rugby tackled a guy who tried to sneak away with my shoes, lol...

Shoe fight

Luckily nothing serious happened and after everyone had a great laugh we continued with the ceremony...

Until finally we were able to celebrate at the reception party in the evening...

Entering at the reception party

Cake cutting

Our first toast as a married couple

Throughout the day our guests wrote us lovely (I think) messages on little papers and my family decided to throw all these messages into a wooden box with a nice bottle of Red, which we had to seal ourselves with nails and hammer. We are not allowed to open this box until in seven years time and then we can enjoy a nicely matured bottle of vino while reading all those wonderful memories from our big day. What a brilliant idea!

Sealing box of memories

We had a fantastic day and before everyone stormed to the dance floor there was even a pretty impressive surprise firework display...

Surprise firework display

Getting married was a lot of fun and it all worked out so much better than we could have hoped for :)

Now we are ready for new adventures...

Comments

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

Older Posts Newer Posts