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.