Skip to content

Latest commit

 

History

History
232 lines (187 loc) · 8.2 KB

MigrateFromGiraffe.md

File metadata and controls

232 lines (187 loc) · 8.2 KB

Migration From Giraffe

While Oxpecker is mostly oriented at developers building brand-new projects, some people might want to migrate their Giraffe applications to Oxpecker to get better support.

Oxpecker API is very similar to Giraffe, however there are some breaking changes you need to be aware of. There is a common list of changes and additional one for migration from Standard Routing (not required if you are already using Endpoint Routing in Giraffe).

Common Changes

HTTP Handler Type

// Giraffe
type HttpFunc = HttpContext -> Task<HttpContext option>
type HttpHandler = HttpFunc -> HttpContext -> Task<HttpContext option>

// Oxpecker
type EndpointHandler = HttpContext -> Task
type EndpointMiddleware = EndpointHandler -> HttpContext -> Task

In Giraffe you mostly work with HttpHandler, which means that every handler takes additional next parameter. In Oxpecker most of the time you work with EndpointHandler and don't need next (except when using EndpointMiddleware).

// Giraffe
let someHandler (str: string) : HttpHandler =
    fun (next: HttpFunc) (ctx: HttpContext) ->
        task {
            return! ctx.WriteTextAsync str
        }

// Oxpecker
let someHandler (str: string) : EndpointHandler =
    fun (ctx: HttpContext) ->
        task {
            return! ctx.WriteText str
        }

View Engine

Oxpecker ViewEngine was written from scratch using computation expressions instead of lists to give you better readability. But from migration perspective, it would be inefficient to rewrite all your views to the new engine. So it's recommended you add additional HttpContext extension method to use Giraffe views in Oxpecker and use it instead of Oxpecker's WriteHtmlView method.

open Oxpecker
open Giraffe.ViewEngine

[<Extension>]
type MyExtensions() =
    [<Extension>]
    static member WriteGiraffeView (ctx: HttpContext, htmlView: XmlNode) =
        let bytes = RenderView.AsBytes.htmlDocument htmlView
        ctx.SetContentType "text/html; charset=utf-8"
        ctx.WriteBytes bytes

HTTP context extensions

Giraffe Oxpecker
WriteBytesAsync WriteBytes
WriteStringAsync removed
WriteTextAsync WriteText
WriteJsonAsync WriteJson
WriteJsonChunkedAsync WriteJsonChunked
WriteXmlAsync removed
WriteHtmlFileAsync removed
WriteHtmlStringAsync WriteHtmlString
WriteHtmlViewAsync WriteHtmlView
BindJsonAsync BindJson
BindXmlAsync removed
BindFormAsync BindForm
BindQueryString BindQuery
TryBindJsonAsync removed
TryBindXmlAsync removed
TryBindFormAsync removed
TryBindQueryString removed
TryGetRouteValue TryGetRouteValue
GetCookieValue TryGetCookieValue
TryGetQueryStringValue TryGetQueryValue
missing TryGetQueryValues
GetFormValue TryGetFormValue
missing TryGetFormValues
TryGetRequestHeader TryGetHeaderValue
missing TryGetHeaderValues
GetXmlSerializer removed
GetHostingEnvironment removed
ReadBodyFromRequestAsync removed
ReadBodyBufferedFromRequestAsync removed

HttpStatusCodeHandlers

The whole module was removed in favour of IResult integration

// Giraffe
Successful.OK myObject next ctx

// Oxpecker
ctx.Write <| TypedResults.Ok myObject

routef function

  • Route parameters must now be enclosed in curly braces
  • Route parameters are now curried
  • Some format characters were changed
// Giraffe
routef "/hello/%s/%O" (fun (a, b) -> doSomething a b)

// Oxpecker
routef "/hello/{%s}/{%O:guid}" (fun a b -> doSomething a b)
Format Char Giraffe Oxpecker
%O Guid (including short GUIDs*) Any object (with constraints)
%u uint64 (formatted as a short ID*) uint64 (regular format)

Short ID and short GUID support was removed, however it could be added later as a %O custom constraint if needed.

Content negotiation

Content negotiation was removed in Oxpecker.

Response caching

Several helpers were removed for more flexibility

// Giraffe
responseCaching
    (Public (TimeSpan.FromSeconds (float 5)))
    (Some "Accept, Accept-Encoding")
    (Some [| "query1"; "query2" |])

// Oxpecker
responseCaching
    (Some <| CacheControlHeaderValue(MaxAge = TimeSpan.FromSeconds(5), Public = true))
    (Some "Accept, Accept-Encoding")
    (Some [| "query1"; "query2" |])

Other changes

  • strOption was dropped
  • readFileAsStringAsync was dropped
  • custom computation expressions for option and result were dropped

Migrate from Standard Routing

The main difference between Standard and Endpoint routing is that in Standard routing every route is tried out sequentially, while in Endpoint routing all possible matches are processed at once. Standard routing is essentially a sequential chain of monadic binds, while Endpoint routing is buiding a map of routes and letting EndpointRouting middleware handle it.

Routing configuration

// Giraffe
let webApp =
    (choose [
        GET_HEAD >=> routef "/hello/%s" (fun name -> text $"Hello {name}!")
        GET >=> (choose [
            route "/foo" >=> setHttpHeader "X-Version" "1" >=> text "Bar"
            subRoute "/v2" >=> (choose [
                route "/foo" >=> text "Bar2"
            ])
        ])
    ])

// Oxpecker
let webApp = [
    GET_HEAD [ routef "/hello/{%s}" (fun name -> text $"Hello {name}!") ]
    GET [
        route "/foo" (setHttpHeader "X-Version" "1" >=> text "Bar")
        subRoute "/v2" [
            route "/foo" <| text "Bar2"
        ]
    ]
]

Dropped functions

  • routeCi (EndpointRouting has case-insensitive routing by default)
  • routex (use regex route constraints instead)
  • routexp
  • routeCix
  • routeCif
  • routeBind
  • routeStartsWith
  • routeStartsWithCi
  • subRouteCi
  • subRoutef (use subRoute + TryGetRouteValue extension method)
  • routePorts

Setup

Here is the app and services configuration for Giraffe

// Giraffe
let configureApp (appBuilder: IApplicationBuilder) =
    appBuilder
        .UseGiraffe(webApp)

let configureServices (services: IServiceCollection) =
    services
        .AddGiraffe() |> ignore

And here is the same configuration for Oxpecker

// Oxpecker
let configureApp (appBuilder: IApplicationBuilder) =
    appBuilder
        .UseRouting()
        .UseOxpecker(endpoints)

let configureServices (services: IServiceCollection) =
    services
        .AddRouting()
        .AddOxpecker() |> ignore