Thursday, April 16, 2015

Basic OWIN Self Host With F#

I’m still very much an F# noob, but yesterday I thought I’d use it to write a little stub web service for a project I’m currently working on. I simply want to respond to any POST request to my service. I don’t need routing, or any other ‘web framework’ pieces. I just wanted to use the Microsoft.AspNet.WebApi.OwinSelfHost package to create a little web service that runs inside a console program.

First create a new F# console project. Then install the self host package:

    Microsoft.AspNet.WebApi.OwinSelfHost

Note that this will also install various WebApi pieces which we don’t need here, so we can go ahead and uninstall them:

    uninstall-package Microsoft.AspNet.WebApi.OwinSelfHost
    uninstall-package Microsoft.AspNet.WebApi.Owin
    uninstall-package Microsoft.AspNet.WebApi.Core
    uninstall-package Microsoft.AspNet.WebApi.Client

My requirement is to simply take any POST request to the service, take the post body and transform it in some way (that’s not important here), and then return the result in the response body.

So first, here’s a function that takes a string and returns a string:

    let transform (input: string) =
        sprintf "%s transformed" input

Next we’ll write the OWIN start-up class. This needs to be a class with a single member, Configuration, that takes an IAppBuilder:

    open Owin
    open Microsoft.Owin
    open System
    open System.IO
    open System.Threading.Tasks

    type public Startup() = 
        member x.Configuration (app:IAppBuilder) = app.Use( ... ) |> ignore

We need something to pass into the Use method on IAppBuilder. The Use method looks like this:

    public static IAppBuilder Use(
        this IAppBuilder app,
        Func<IOwinContext, Func<Task>, Task> handler
    )

So we need a handler with the signature Func<IOwinContext, Func<Task>, Task>. Since F# lambdas cast directly to Func<..> delegates, we simply use lots of type annotations and write a function which looks like this:

    let owinHandler = fun (context:IOwinContext) (_:Func) -> 
        handleOwinContext context; 
        Task.FromResult(null) :> Task

Note that this is running synchronously. We’re just returning a completed task.

Now lets look at the handleOwinContext function. This simply takes the IOwinContext, grabs the request, checks that it’s a ‘POST’, and transforms the request stream into the response stream using our transform function:

    let handleOwinContext (context:IOwinContext) =

        use writer = new StreamWriter(context.Response.Body)

        match context.Request.Method with
        | "POST" -> 
            use reader = new StreamReader(context.Request.Body)
            writer.Write(transform(reader.ReadToEnd()))
        | _ ->
            context.Response.StatusCode <- 400
            writer.Write("Only POST")

Now all we need to do is register our Startup type with the OWIN self host in our Program.Main function:

open System
open Microsoft.Owin.Hosting

[]
let main argv = 

    let baseAddress = "http://localhost:8888"

    use application = WebApp.Start<Startup.Startup>(baseAddress)

    Console.WriteLine("Server running on {0}", baseAddress)
    Console.WriteLine("hit <enter> to stop")
    Console.ReadLine() |> ignore
    0

And we’re done. Now let’s try it out with the excellent Postman client, just run the console app and send a POST request to http://localhost:8888/:

Postman_owin_self_host_fsharp

Full source code in this Gist.

No comments: