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).
// 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
}
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
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 |
The whole module was removed in favour of IResult
integration
// Giraffe
Successful.OK myObject next ctx
// Oxpecker
ctx.Write <| TypedResults.Ok myObject
// 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*) |
Guid (requires guid constraint) |
%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 was removed in Oxpecker.
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" |])
strOption
was droppedreadFileAsStringAsync
was droppedThe 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.
// 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"
]
]
]
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
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