Introduction
Casablanca is the quasi-official C++ REST library from Microsoft published as an open source
project on CodePlex. And I say "quasi" as it does not come with Visual C++ by
default, but for all you know that may change in future. The VC++ team has
backed it strongly and has recommended using it for all your REST access
requirements.
Casablanca allows you to write native code to access REST services and uses
an asynchronous approach to consuming HTTP and JSON based services. There are
extensions that allow you to write Windows 8 store applications in Casablanca,
but you can use it in desktop apps as well. The code is written in a portable
manner, so you can use it from Linux too, should you want to.
This article quickly demonstrates a skeletal ASP.NET MVC 4 REST web service
that is consumed by C++ code that uses Casablanca to do the four common HTTP
operations - GET, POST, PUT, and DELETE. It also shows how to parse and create
JSON, and also how to use the PPL extensions to write asynchronous code.
The skeletal web service
I wanted to keep the example simple, so this service uses a very simple
business object called Member
.
public class Member
{
public int Id { get; set; }
public string Name { get; set; }
public string Sport { get; set; }
}
Instead of reading and witting from an actual database or backend, I have a
mock storage class that can add, edit, fetch, and delete Member
objects.
public class Members
{
private Collection<Member> members = new Collection<Member>();
private int nextId = 1;
public Member Add(string name, string sport)
{
var member = new Member()
{
Id = nextId++,
Name = name,
Sport = sport
};
members.Add(member);
return member;
}
public IEnumerable<Member> GetAll()
{
return members;
}
public Member Get(int id)
{
return members.FirstOrDefault(m => m.Id == id);
}
public Member Update(int id, string name, string sport)
{
var item = Get(id);
if (item != null)
{
item.Name = name;
item.Sport = sport;
return item;
}
return null;
}
public bool Delete(int id)
{
var item = Get(id);
if (item != null)
{
members.Remove(item);
return true;
}
return false;
}
}
And here's the controller.
public class ValuesController : ApiController
{
static ValuesController()
{
members.Add("Nish", "Tennis");
members.Add("Andrew", "Baseball");
}
private static Members members = new Members();
public IEnumerable<Member> Get()
{
return members.GetAll();
}
public Member Get(int id)
{
return members.Get(id);
}
public int Post(dynamic data)
{
return members.Add((string)data.name, (string)data.sport).Id;
}
public Member Put(int id, dynamic data)
{
return members.Update(id, (string)data.name, (string)data.sport);
}
public bool Delete(int id)
{
return members.Delete(id);
}
}
Making GET
calls and parsing JSON
Typically, these are the include files you'd need to use Casablanca.
#include <http_client.h>
#include <ppltasks.h>
#include <json.h>
Here's the C++ version of the business object.
class Member
{
public:
int Id;
std::wstring Name;
std::wstring Sport;
void Display()
{
std::wcout << Id << L", " << Name << L", " << Sport << std::endl;
}
};
I added a Display
method for logging/display purposes. I also
added a helper class to create a Member
object given JSON data.
enum FieldValue {Id, Name, Sport };
class MemberGenerator
{
std::map<std::wstring, FieldValue> fieldMap;
Member member;
public:
MemberGenerator()
{
fieldMap[L"Id"] = FieldValue::Id;
fieldMap[L"Name"] = FieldValue::Name;
fieldMap[L"Sport"] = FieldValue::Sport;
}
void SetField(std::wstring name, json::value value)
{
switch(fieldMap[name])
{
case FieldValue::Id:
member.Id = value.as_integer();
break;
case FieldValue::Name:
member.Name = value.as_string();
break;
case FieldValue::Sport:
member.Sport = value.as_string();
break;
}
}
Member GetMemberFromJson(json::value jsonValue)
{
for(auto iterInner = jsonValue.cbegin(); iterInner != jsonValue.cend(); ++iterInner)
{
const json::value &propertyName = iterInner->first;
const json::value &propertyValue = iterInner->second;
SetField(propertyName.as_string(), propertyValue);
}
return member;
}
};
I wish C++ had reflection the way C# has (or rather .NET has). That'd have
made writing this much easier and cleaner. But this is close enough and is
probably better from a performance perspective. The GetMemberFromJson
gets a json::value
object as its argument. The json::value
class is basically a C++ abstraction over a JSON value. In my example, this will
be a composite JSON value that contains the properties of the Member
object. Using cbegin
and cend
, we iterate through the
composite object. The iterator is an std::vector
of an
std::pair
of
json::value
objects. The pair represents the
property name and its associated value. The
SetField
method then
looks up the property name and for each property, we know the type and thus call
one of the
as_xxx()
methods which converts the JSON value into the
requested type. Obviously, a more real world scenario would have multi-level
nested business objects, so you'd need a more involved conversion framework, but
I would imagine that the core approach would remain very similar to what I did
there.
Here's the code that does a GET call to get all objects. This is
basically the implementation for GET api/values.
pplx::task<void> GetAll()
{
return pplx::create_task([]
{
http_client client(L"http://localhost:5540/api/values");
return client.request(methods::GET);
}).then([](http_response response)
{
if(response.status_code() == status_codes::OK)
{
return response.extract_json();
}
return pplx::create_task([] { return json::value(); });
}).then([](json::value jsonValue)
{
if(jsonValue.is_null())
return;
MemberGenerator generator;
for(auto iterArray = jsonValue.cbegin(); iterArray != jsonValue.cend(); ++iterArray)
{
const json::value &arrayValue = iterArray->second;
auto member = generator.GetMemberFromJson(arrayValue);
member.Display();
}
});
}
The http_client
class, rather unsurprisingly named, is the core
class that handles the HTTP connection to the web service. The request
method sends the HTTP request asynchronously, and I've specified this to be a
GET request. The continuation gets an http_response
object that
represents the response from the server. (These methods and types are so
lucidly named that I feel like an idiot repeating things. I mean saying things
like - the http_response class represents an HTTP response. Oh well!) It's
got methods to get the body, headers, status code, etc. Once I verify that the
response code was 200, I call the extract_json
method, also
asynchronous. When that's completed, the continuation receives a
json::value
object. In this case, I know it's an array of JSON
values representing
Member
objects, and so I iterate through the
list and extract the
Member
objects using my object conversion
class. Here's the code that does a
GET api/values/id call.
pplx::task<void> Get(int id)
{
return pplx::create_task([id]
{
std::wstringstream ws;
ws << L"http://localhost:5540/api/values/" << id;
http_client client(ws.str());
return client.request(methods::GET);
}).then([](http_response response)
{
if(response.status_code() == status_codes::OK)
{
return response.extract_json();
}
return pplx::create_task([] { return json::value(); });
}).then([](json::value jsonValue)
{
if(jsonValue.is_null())
return;
MemberGenerator generator;
auto member = generator.GetMemberFromJson(jsonValue);
member.Display();
});
}
It's quite similar except the URL now includes the id to fetch and the JSON
response is for a single Member
object.
Submitting a POST
Here's code showing how to POST data to the service.
pplx::task<int> Post()
{
return pplx::create_task([]
{
json::value postData;
postData[L"name"] = json::value::string(L"Joe Smith");
postData[L"sport"] = json::value::string(L"Baseball");
http_client client(L"http://localhost:5540/api/values");
return client.request(methods::POST, L"",
postData.to_string().c_str(), L"application/json");
}).then([](http_response response)
{
if(response.status_code() == status_codes::OK)
{
auto body = response.extract_string();
std::wcout << L"Added new Id: " << body.get().c_str() << std::endl;
return std::stoi(body.get().c_str());
}
return 0;
});
}
The json::value
class has overloaded [] operator
s,
so you can use an array-like syntax to set data. When making the request
call, you need to specify POST, provide the data to sent, and set the
content-type to application/json. The web service returns back the ID
of the newly added object, so there's code there to parse that and return the
int
value.
Making PUT
and DELETE
calls
The PUT implementation is very similar to POST, except you pass
an ID.
pplx::task<void> Put(int id)
{
return pplx::create_task([id]
{
json::value postData;
postData[L"name"] = json::value::string(L"Joe Y Smith");
postData[L"sport"] = json::value::string(L"Baseball 2");
std::wstringstream ws;
ws << L"http://localhost:5540/api/values/" << id;
http_client client(ws.str());
return client.request(methods::PUT, L"",
postData.to_string().c_str(), L"application/json");
}).then([](http_response response)
{
if(response.status_code() == status_codes::OK)
{
auto body = response.extract_string();
std::wcout << L"Updated: " << body.get().c_str() << std::endl;
}
});
}
The DELETE is fairly simple too.
pplx::task<void> Delete(int id)
{
return pplx::create_task([id]
{
std::wstringstream ws;
ws << L"http://localhost:5540/api/values/" << id;
http_client client(ws.str());
return client.request(methods::DEL);
}).then([](http_response response)
{
if(response.status_code() == status_codes::OK)
{
auto body = response.extract_string();
std::wcout << L"Deleted: " << body.get().c_str() << std::endl;
}
});
}
This actually returns a bool
, but I am not parsing it and merely
displaying it to the console. But you can really do whatever you want with it.
At this point, it's fairly easy to chain all these calls together and do
something like this.
GetAll().then([]
{
Post().then([](int newId)
{
Get(newId).then([newId]
{
Put(newId).then([newId]
{
GetAll().then([newId]
{
Delete(newId).then([]
{
GetAll();
});
});
});
});
});
});
That's all. As usual, do post any feedback or criticism via the forum at the
bottom of this article.
References
History
- June 6th, 2013 - Article published