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/:
Full source code in this Gist.
No comments:
Post a Comment