Skip to content

Instantly share code, notes, and snippets.

@Aaronontheweb
Created April 20, 2025 19:14
Show Gist options
  • Save Aaronontheweb/dd67dcc45d97fe081d43f9d872be9e74 to your computer and use it in GitHub Desktop.
Save Aaronontheweb/dd67dcc45d97fe081d43f9d872be9e74 to your computer and use it in GitHub Desktop.
Falco Static Files
module Program
open Falco
open Falco.Routing
open Microsoft.AspNetCore.Builder
let wapp = WebApplication.Create()
wapp.UseRouting()
.UseDefaultFiles()
.UseStaticFiles()
.UseFalco([
])
.Run(Response.ofPlainText "Not found")
module Program
open Falco
open Falco.Routing
open Microsoft.AspNetCore.Builder
let wapp = WebApplication.Create()
wapp.UseRouting()
.UseDefaultFiles()
.UseStaticFiles()
.Run(Response.ofPlainText "Not found")
@stuartbright
Copy link

stuartbright commented Apr 20, 2025

This works for me:


open Falco
open Falco.Routing
open Microsoft.AspNetCore.Builder

let wapp = WebApplication.Create()

wapp.UseRouting()
    .UseDefaultFiles()
    .UseStaticFiles()
    .UseFalco([
        
    ])
    |> ignore
wapp.Run(Response.ofPlainText "Not found")

@stuartbright
Copy link

I think the difference is that in the working example the Run method is called on a WebApplication, whereas in the broken example it's called on an IApplicationBuilder.

@Aaronontheweb
Copy link
Author

Thanks @stuartbright - I appreciate your time. I'll try this locally!

@Aaronontheweb
Copy link
Author

Can confirm, works great @stuartbright

@pimbrouwers
Copy link

Thanks for chiming in Stuart with a solution.

Both program files fall through because they are hitting an ASP.NET extension, Microsoft.AspNetCore.Builder.RunExtensions.Run, which adds a terminal middleware to the pipeline. This has a dramatically different effect than Microsoft.AspNetCore.Builder.WebApplication.Run which runs the application and blocks the calling thread. The latter is not being called here in either example, so the host never starts up.

A lot of the older extension methods like UseStaticFiles() and UseDefaultFiles() are hung off IApplicationBuilder, where the endpoint methods are hung off WebApplication. The dichotomy makes pipelining the builder next to impossible. In C# you don't feel the sting of this as much because of void returns. To salve this, Falco comes with a couple shims to avoid an incessant amount of |> ignore calls. In both examples you actually use one, UseRouting() (required to activate Falco which depends on ASP.NET endpoints).

The most important of these are WebApplication.Use and WebApplication.UseIf which allow you to compose a pipeline entirely driven by WebApplication. Throughout the documentation I point out that the open Microsoft.AspNetCore.Builder import adds many useful extensions, many of these are complete unknown to most devs, but are the underpinning to the builder extension method. They are a bit esoteric, but they are really useful in F#-land. A quick example:

// instead of 
wapp.UseDefaultFiles()
    .UseStaticFiles()

// try:
wapp.Use(fun (appl : IApplicationBuilder) ->
    appl.UseDefaultFiles()
        .UseStaticFiles())

// or better yet,
wapp.Use(DefaultFilesExtensions.UseDefaultFiles)
    .Use(StaticFileExtensions.UseStaticFiles)

The HelloWorldMVC example shows how to use this effectively.

@Aaronontheweb
Copy link
Author

thanks @pimbrouwers !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment