Click here to Skip to main content
12,506,338 members (52,932 online)
Click here to Skip to main content
Add your own
alternative version

Stats

44.9K views
32 bookmarked
Posted

Using Casablanca to consume a REST API

, 6 Jun 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
The article walks through using the Casablanca REST API library to consume a REST web service from C++ code

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();

    // GET api/values
    public IEnumerable<Member> Get()
    {
        return members.GetAll();
    }

    // GET api/values/id
    public Member Get(int id)
    {
        return members.Get(id);
    }

    // POST api/values
    public int Post(dynamic data)
    {
        return members.Add((string)data.name, (string)data.sport).Id;
    }

    // PUT api/values/id
    public Member Put(int id, dynamic data)
    {
        return members.Update(id, (string)data.name, (string)data.sport);
    }

    // DELETE api/values/id
    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 [] operators, 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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Nish Nishant
United States United States
Nish Nishant is a Software Architect/Consultant based out of Columbus, Ohio. He has over 16 years of software industry experience in various roles including Lead Software Architect, Principal Software Engineer, and Product Manager. Nish is a recipient of the annual Microsoft Visual C++ MVP Award since 2002 (14 consecutive awards as of 2015).

Nish is an industry acknowledged expert in the Microsoft technology stack. He authored
C++/CLI in Action for Manning Publications in 2005, and had previously co-authored
Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his
WordPress blog. Nish is vastly experienced in team management, mentoring teams, and directing all stages of software development.

Contact Nish : You can reach Nish on his google email id voidnish.

Website and Blog

You may also be interested in...

Pro
Pro

Comments and Discussions

 
Questionjson and casablanca Pin
Member 1195001931-Aug-15 23:22
memberMember 1195001931-Aug-15 23:22 
QuestionHow to use C++ REST SDK with ANSI charset ? Pin
Nacereddine25-Feb-15 3:38
professionalNacereddine25-Feb-15 3:38 
SuggestionNice Intro - Part2? Pin
JoeCane21-Jun-13 6:10
memberJoeCane21-Jun-13 6:10 
GeneralRe: Nice Intro - Part2? Pin
Nish Sivakumar21-Jun-13 13:21
sitebuilderNish Sivakumar21-Jun-13 13:21 
GeneralRe: Nice Intro - Part2? Pin
JoeCane24-Jun-13 5:20
memberJoeCane24-Jun-13 5:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160927.1 | Last Updated 6 Jun 2013
Article Copyright 2013 by Nish Nishant
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid