This is a outline of how you can setup a simple RESTlike client/server communication using WebAPI and Restsharp. As you will see F# records are great for this: equality out of the box, read-only by default but still nicely serializeable.
I hosted the code on github for your convenience and the rest of the post will just walk you through the different parts of the solution there.
the common parts
I like to have parts of the project that are shared by different components in a Contracts
project.
For a starter this is rather slim: I only added an error-monad for convenience, a single message to be passed between the server/clients (Echo
) and a handler-type Handler.T
that you can use to inject message-handling on the client into the server components as a parameter to the self-hosting:
module Handler = type T = { OnEcho : string -> Async<string> } let initial : T = { OnEcho = fun s -> async { return s } }
I usually setup this pieces of infrastructure so that you can get a default-implementation (initialHandler
) and only overwrite the parts you want to change like this:
let myHandler = { Handler.initial with OnEcho = fun s -> ... }
Then only notable part on the model of the Echo
message:
[<CLIMutable>] type Echo = { message : string }
is the CLIMutable
attribute – it basically tells the F# compiler to produce a constructor without parameters and setters for the records properties in the background. You will never see those in F# (so for all you care the record is immutable) but all those C# frameworks relying on having parameterless constructors and public setters (like some serializiers 😉 ) will work out-of-the box finally (well almost – see below).
the server-parts
I am using WebAPI with an selfhosting option here but of course you can extent it to anything you like. Right now there are only two parts: a controller named MyApiController
(ApiController
was already used 😉 ) and a SelfHosting
module.
the controller
The controller is a obvious translation from what you would do in C# to F#. I inject a Handler.T
as an constructor-parameter and am using Task.StartAsTask
to translate the async-handler code into a Task:
type MyApiController (handler : Handler.T) = inherit ApiController () [<HttpGet()>] member this.Echo (msg : string) : Task<HttpResponseMessage> = async { try let! reply = Handler.onEcho handler msg return this.Request.CreateResponse ( HttpStatusCode.OK, { Messages.message = reply }) with | _ as err -> return this.Request.CreateErrorResponse ( HttpStatusCode.InternalServerError, err) } |> Async.StartAsTask
That makes the self-hosting setup more interesting.
self-hosting configuration
getting the injection working
The first stop is to setup a resolver to get a Handler.T
into the controllers. I don’t think a IoC-Container is just to big and so I just set up a new resolver that will construct a MyApiController
just like I want and just reuses the default-resolver everywhere else:
let setupHandlerInjection (handler : Handler.T) (config : #HttpConfiguration) = let resolve (oldResolver : Dependencies.IDependencyResolver) handler (t : System.Type) : obj = try if t = typeof<MyApiController> then new MyApiController (handler) :> _ else oldResolver.GetService t with | _ as err -> failwith err.Message let scope oldResolver = { new Dependencies.IDependencyScope with member __.GetService (serviceType : System.Type) = resolve oldResolver handler serviceType member i.GetServices (serviceType : System.Type) = i.GetService(serviceType) |> Seq.singleton member __.Dispose() = () } let resolver oldResolver = { new Dependencies.IDependencyResolver with member __.BeginScope() = scope oldResolver member __.GetService (serviceType : System.Type) = resolve oldResolver handler serviceType member i.GetServices (serviceType : System.Type) = i.GetService(serviceType) |> Seq.singleton member __.Dispose() = () } config.DependencyResolver <- resolver config.DependencyResolver config
it’s really just ugly boiler-plate code.
Setup XML and Json formatters to produce sensible names
Having the CLIMutable attribute available it should be easy to serialize and deserialize F# records …. right?
Well it turns out there some pitfalls that did cost me some time to research lately.
The problem I had was that the default serializers in WebAPI are fine with F#-records (having the attribute) now but appends @
-characters at each field. Restsharp on the other hand does not nor will it understand this and so if you try to use those two together you will have Null-references all around 🙁
I pulled this from Mark`s blog – big Kudos to him!
What did work out for me is to set this in my HttpConfiguration
(using JSON.net):
config.Formatters .XmlFormatter .UseXmlSerializer <- true config.Formatters .JsonFormatter .SerializerSettings .ContractResolver <- Newtonsoft .Json .Serialization .CamelCasePropertyNamesContractResolver()
For example if you setup a self-hosting service you can do something like this:
let configureFormatters (config : #HttpConfiguration) = config.Formatters .XmlFormatter .UseXmlSerializer <- true config.Formatters .JsonFormatter .SerializerSettings .ContractResolver <- Newtonsoft .Json .Serialization
routes and hosting
The rest is simple – first set up the routes:
let setupRoutes (config : #HttpConfiguration) = config.Routes.MapHttpRoute ( "default", "{action}", { controller = "MyApi"; action = "Echo" } ) |> ignore config
and then plug it all together and start the server:
let hostService (handler : Handler.T) (url : string) = let config = new HttpSelfHostConfiguration(url) |> configureFormatters |> setupHandlerInjection handler |> setupRoutes let server = new HttpSelfHostServer(config) server.OpenAsync().Wait() { new IDisposable with member __.Dispose() = server.CloseAsync().Wait() server.Dispose() config.Dispose() } let hostServiceAt (handler : Handler.T) url = hostService handler url let hostLocalService (handler : Handler.T) (port : int) = sprintf "http://localhost:%d" port |> hostServiceAt handler
As you can see I choose to use the common Disposer
-pattern: the functions will return an IDisposable
that – when disposed – will shutdown the server and clean up the used resources, including the config.
the client-side
Where the server side is using WebAPI I opted for RestSharp on the client side, reusing the Echo
model from above.
There are a few helper-methods in AsyncRequests.fs that deal with translating RestSharp ExecuteAsync
requests into async
-Workflows but that’s really only using Async.FromContinuations
and breaking down the ResponseStatus
you get from RestSharper.
The only exotic part here is me using ErrorOrSuccess
(the error-monad I mentioned above and defined in the Contracts
project) to mark the results as either successful or failed. In the success case you get the returned value and in the failed case you will get an exception back instead.
The other file is providing an API
:
module Api = type IApi = abstract echo : string -> Async<Messages.Echo> let private echo (url : Url) (echo : string) : Async<Messages.Echo> = async { let! response = sprintf "/Echo?msg=%s" echo |> getAsync url return ErrorOrSuccess.valueOf response } let createApi (url : Url) : IApi = { new IApi with member __.echo msg = echo url msg }
Please note that I choose to just reraise the exception (ErrorOrSuccess.valueOf
will do this) if there was an problem getting the result back from the server. In a real implementation you might want to return and use a ErrorOrSccess<Messages.Echo>
for your handlers instead.
some tests
The rest of the solution consists of a test project with two tests:
- one to tests just the controller (as there is some non-trivial setup necessary)
- an end-to-end integration-test you will have to run with elevated rights because it will map the controller to
http://localhost:8084
.
Pingback: Dew Drop – March 5, 2015 (#1968) | Morning Dew()