It’s very easy to get started on an F# Web API application thanks to Dan Mohl‘s excellent F# C# MVC 4 template. After installing the template, create an F# and C# Web Application (ASP.NET MVC 4) in Templates .. Visual F# .. ASPNET and select WebApi Project in the next dialog. You start with 2 projects, a C# MVC 4 project (WebApi) and an F# Web API project (WebAppApi)
If you look in the ValuesController.cs in the WebAppApi project, you’ll see the following code:
namespace FsWeb.Controllers
open System.Web
open System.Web.Mvc
open System.Net.Http
open System.Web.Http
type ValuesController() =
inherit ApiController()
// GET /api/values
member x.Get() = [| "value1"; "value2" |] |> Array.toSeq
// GET /api/values/5
member x.Get (id:int) = "value"
// POST /api/values
member x.Post ([<FromBody>] value:string) = ()
// PUT /api/values/5
member x.Put (id:int) ([<FromBody>] value:string) = ()
// DELETE /api/values/5
member x.Delete (id:int) = ()
To test this you can run this project and test the API using Fiddler, or alternatively you can follow Scott Hanselman’s post on installing HTTPie. (Replace [port] with whatever is configured for your application.)
C:\>http http://localhost:port/api/values
HTTP/1.1 200 OK
Content-Length: 19
Content-Type: application/json; charset=utf-8
Date: Tue, 09 Oct 2012 21:50:53 GMT
Server: Microsoft-IIS/8.0
[
"value1",
"value2"
]
Let’s try something more complex. Say our model is a record:
type Value = { Id: int; Name: string; }
(Admittedly it’s not that complex but it will suit our purpose.)
Now change the Get() method on the ValuesController
member x.Get() = [|
{ Id=1; Name="value1" };
{ Id=2; Name="value2" } |] |> Array.toSeq
Running HTTPie as before, we get the following JSON output:
C:\>http http://localhost:port/api/values
HTTP/1.1 200 OK
Content-Length: 55
Content-Type: application/json; charset=utf-8
Date: Tue, 09 Oct 2012 22:54:52 GMT
Server: Microsoft-IIS/8.0
[
{
"Id@": 1,
"Name@": "value1"
},
{
"Id@": 2,
"Name@": "value2"
}
]
Unfortunately the result is not exactly how we want it. What’s happening is that JSON.NET is serializing the field names of the record rather than the properties, hence the @ signs at the end of each member. To fix this, we can add a reference to the Newtonsoft.JSON nuget package in the WebAppApi project, and mark the record with a JsonObject attribute
open Newtonsoft.Json
[<CLIMutable>]
[<JsonObject(MemberSerialization=MemberSerialization.OptOut)>]
type Value = { Id: int; Name: string; }
Now calling the API again, we get the following JSON response
[
{
"Id": 1,
"Name": "value1"
},
{
"Id": 2,
"Name": "value2"
}
]
If you only ever intend your clients to use JSON to talk to your API you could consider yourself done. But what does the response look like as XML?
C:\>http http://localhost:port/api/values Accept:application/xml
HTTP/1.1 200 OK
Content-Length: 291
Content-Type: application/xml; charset=utf-8
Date: Wed, 10 Oct 2012 22:44:22 GMT
Server: Microsoft-IIS/8.0
<ArrayOfValue
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/FsWeb.Controllers">
<Value>
<Id_x0040_>1</Id_x0040_>
<Name_x0040_>value1</Name_x0040_>
</Value>
<Value>
<Id_x0040_>2</Id_x0040_>
<Name_x0040_>value2</Name_x0040_>
</Value>
</ArrayOfValue>
To serialize correctly as both XML and Json we can use the DataContract attribute (in System.Runtime.Serialization.dll)
open System.Runtime.Serialization
[<CLIMutable>]
[<DataContract>]
type Value = {
[<DataMember>]Id: int;
[<DataMember>]Name: string; }
Now our response looks better:
<ArrayOfValue
xmlns="http://schemas.datacontract.org/2004/07/FsWeb.Controllers"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Value>
<Id>1</Id>
<Name>value1</Name>
</Value>
<Value>
<Id>2</Id>
<Name>value2</Name>
</Value>
</ArrayOfValue>
Unfortunately this technique requires us to decorate each and every field with the DataMember attibute. I’m sure there must be a better way.