Click here to Skip to main content
Click here to Skip to main content

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 Sivakumar

United States United States
Nish is a real nice guy who has been writing code since 1990 when he first got his hands on an 8088 with 640 KB RAM. Originally from sunny Trivandrum in India, he has been living in various places over the past few years and often thinks it’s time he settled down somewhere.
 
Nish has been a Microsoft Visual C++ MVP since October, 2002 - awfully nice of Microsoft, he thinks. He maintains an MVP tips and tricks web site - www.voidnish.com where you can find a consolidated list of his articles, writings and ideas on VC++, MFC, .NET and C++/CLI. Oh, and you might want to check out his blog on C++/CLI, MFC, .NET and a lot of other stuff - blog.voidnish.com.
 
Nish loves reading Science Fiction, P G Wodehouse and Agatha Christie, and also fancies himself to be a decent writer of sorts. He has authored a romantic comedy Summer Love and Some more Cricket as well as a programming book – Extending MFC applications with the .NET Framework.
 
Nish's latest book C++/CLI in Action published by Manning Publications is now available for purchase. You can read more about the book on his blog.
 
Despite his wife's attempts to get him into cooking, his best effort so far has been a badly done omelette. Some day, he hopes to be a good cook, and to cook a tasty dinner for his wife.

Comments and Discussions

 
QuestionHow to use C++ REST SDK with ANSI charset ? PinprofessionalNacereddine25-Feb-15 4:38 
SuggestionNice Intro - Part2? PinmemberJoeCane21-Jun-13 7:10 
GeneralRe: Nice Intro - Part2? PinsitebuilderNish Sivakumar21-Jun-13 14:21 
GeneralRe: Nice Intro - Part2? PinmemberJoeCane24-Jun-13 6:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    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
Web04 | 2.8.150301.1 | Last Updated 6 Jun 2013
Article Copyright 2013 by Nish Sivakumar
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid