Skip to content

Releases: falcoframework/Falco

v3.1

27 Jul 17:19
b7d334f

Choose a tag to compare

webHost Builder Improvements

In previous versions, the minimalistic web host builder allowed devs to get running quickly and when needed enabled full customization via the configure custom operation. This meant that anything beyond a toy project, required a fair amount of repetitive boilerplate setup code.

To combat this, several custom operations have been added, which semantically match the relevant area (i.e., add_service for services, use_middleware for middleware). The goal is to avoid the need to engage in a full-fledged configuration, although the configure method still exists which will override all other customizations which also creates a kind backward compatibility.

In addition, many common operations have been explicitly mapped: use_static_files, use_https, use_compression etc.

An example of the new builder in action can be found in the docs or samples.

configuration Builder Added

A thin wrapper around ConfigurationBuilder exposing a clean API for reading configuration values.

open Falco.HostBuilder

[<EntryPoint>]
let main args =    
    let env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")
    
    let config = configuration args {
        add_env
        required_json "appsettings.json"
        optional_json (String.Concat([|"appsettings."; env; ".json"|]))
    }

Utility Additions

  • StringUtils.stringf
  • StringUtils.strSplit
  • CookieCollectionReader
  • Auth.getClaimValue
  • Auth.hasScope

New Request functionality

  • Request.getCookie
  • Request.tryBindCookie
  • Request.streamForm
  • Request.tryBindFormStream

New Request HttpHandler's

  • Request.mapCookie
  • Request.bindCookie
  • Request.ifAuthenticatedWithScope

New Request HttpHandler's for streaming multipart data

Two particular fundamental handlers have been added to the Request module, to support multipart form data streaming for large uploads which Microsoft defines large uploads as anything > 64KB.

  • Request.bindFormStream
  • Request.bindFormStreamSecure
  • Request.mapFormStream
  • Request.mapFormStreamSecure

New Response HttpHandler's

Handlers have been added to support binary responses, both inline and attachment. Both asynchronously buffer data into the response body.

  • Response.ofBinary
  • Response.ofAttachment

v3.0

28 Nov 13:24

Choose a tag to compare

With .NET 5.0 finally here, it seemed like a good time to move to v3.x.x which will support both the netcoreapp3.1 and net5.0 build targets. The major version upgraded represented an opportunity to re-evaulate certain features of the API and determine if there were any missed opportunties.

The most practical upgrade was surrounding IHost creating, for which a computation expression has been included: webHost args { ... }. With that came registration & activation extension methods for IServiceCollection and IApplicationBuilder respectively. They are aptly named services.AddFalco() and app.UseFalco(endpoints). The global exception handler hook has been renamed app.UseFalcoExceptionHandler(...).

How you interact with header and route values, now directly matches interactions with queries and forms, all enabled by the StringCollectionReader. A third set of methods was added to this class supporting "get or default" functionality.

Please note, that the ? dynamic operator has been removed.

Listed below is the full list of additions and removals:

Additions

  • IServiceCollection.AddFalco
  • IServiceCollection.AddFalco (routeOptions : RouteOptions -> unit)
  • IApplicationBuilder.UseFalco (endpoints : HttpEndpoint list)
  • IApplicationBuilder.UseFalcoExceptionHandler (exceptionHandler : HttpHandler)
  • QueryCollectionReader replacing direct usage of StringCollectionReader
  • HeaderCollectionReader
  • RouteCollectionReader

Removals

Extensions

  • HttpRequest.GetHeader
  • HttpRequest.GetRouteValues
    • HttpRequest.GetRouteReader

Exceptions

  • type ExceptionHandler
  • type ExceptionHandlingMiddleware

Host module

  • Host.defaultExceptionHandler
  • Host.defaultNotFoundHandler
  • Host.startWebHostDefault
  • Host.startWebHost
  • IApplicationBuilder.UseHttpEndpoints (endpoints : HttpEndpoint list)
    • replaced by IApplicationBuilder.UseFalco (endpoints : HttpEndpoint list)

Request module

  • Request.getHeader
  • Request.getRouteValues
    • replace by Request.getRoute
  • Request.tryGetRouteValue

StringCollectionReader

  • ? dynamic operator

v2.0

12 Jul 00:25
0a18981

Choose a tag to compare

Release Notes

  • The markup DSL is qualified instead of bare functions.
    • Html.h1 vs h1
    • Attr.class' vs _class
  • Handlers considered end-to-end processors of a request.
    • Continuations are still possible by creating new HttpHandler function which accept another HttpHandler as a parameter.
    • As a result of this change, performance has increased.
  • Host setup functions have been added.
    • These functions (startWebHost and startWebHostDefault) simplify IHost creation.
  • Modular interop with the HttpContext.
    • Optional online extension methods are used to enrich the base library. This functionality is now exposed in a modular fashion (i.e. Response.withStatusCode vs ctx.SetStatusCode).
      • HttpContext based modules exist for: Request, Response, Auth & Xss

Migration Guide

This is a general guide on migrating v1.x.x code to v2.0.0. Both sample apps have been updated and serve as more complete references.

  • The definition of an HttpHandler (HttpContext -> Task) now resembles that of a native RequestDelegate
  • A new definition for any non-IO based modifications to the HttpResponse, called HttpResponseModifier with a definition of HttpContext -> HttpContext
  • Dealing with either the HttpRequest or HttpResponse is now achieved through the Request and Response modules respectively.

An example:

// v1.x.x
let notFound : HttpHandler =
    setStatusCode 404
    >=> textOut "Not Found"

// v2.0.0
let notFound : HttpHandler =
    Response.withStatusCode 404
    >> Response.ofPlainText "Not found"

Another example:

// v.1.x.x
let helloHandler : HttpHandler =
    fun next ctx ->        
        let greeting =
            ctx.tryGetRouteValue "name"
            |> Option.defaultValue "someone"
            |> sprintf "hi %s" 

        textOut

// v2.0.0
let helloHandler : HttpHandler =
    fun ctx ->        
        let greeting =
            Request.tryGetRouteValue "name" ctx 
            |> Option.defaultValue "someone"
            |> sprintf "hi %s" 

        Response.ofPlainText greeting ctx

Another example:

// v1.x.x
let exampleTryBindFormHandler : HttpHandler =
    tryBindForm 
        (fun r ->
            Ok {
              FirstName = form?FirstName.AsString()
              LastName  = form?LastName.AsString()
              Age       = form?Age.AsInt16()      
            })
        errorHandler 
        successHandler

// v2.0.0
let exampleTryBindFormHandler : HttpHandler =
    fun ctx ->
        let bindForm form =     
            {
              FirstName = form?FirstName.AsString()
              LastName  = form?LastName.AsString()
              Age       = form?Age.AsInt16()      
            }

        let respondWith =
            match Request.tryBindForm (bindForm >> Result.Ok) ctx with
            | Error error -> Response.ofPlainText error
            | Ok model    -> Response.ofPlainText (sprintf "%A" model)

        respondWith ctx

Markup

  • Falco.ViewEngine becomes Falco.Markup
  • Element level items now reside in a module called Elem. Thus div becomes Elem.div
    • ** You can import Falco.Markup.Elem and use without Elem. prefix
  • Element attributes are now reside in a module calls Attr. This _class becomes Attr.class'
    • ** Take note of the trailing apostrophe, which is used to delimit reserved keywords like class
  • Text elements now reside in a module called Text. Thus raw becomes Text.raw
    • ** You can import Falco.Markup.Text and use without Text. prefix

An example:

// v1.x.x
let doc = 
    html [] [
            head [] [            
                    title [] [ raw "Sample App" ]                                                    
                ]
            body [] [                     
                    h1 [] [ raw "Sample App" ]
                ]
        ] 

// v2.0.0
let doc = 
    Elem.html [ Attr.lang "en" ] [
            Elem.head [] [                    
                    Elem.title [] [ Text.raw "Sample App" ]                                                            
                ]
            Elem.body [] [                     
                    Elem.main [] [
                            Elem.h1 [] [ Text.raw "Sample App" ]
                        ]
                ]
        ]