Making HTTP REST Request in C++

Introduction

Today, I am going to show you on how to make HTTP request to a REST server using C++ Requests library by Huu Nguyen. Mr Nguyen is heavily influenced by Python Requests design philosoply when writing C++ Requests. Those who had used or familiar with Python Requests, should feel right at home with C++ Requests.

To demostrate our client code, we need a web server that we can make our request to so in our case, we’ll use ASP.NET Web API version 2 to implement our CRUD API. The web server is not this article’s focus but I shall still devote some time to explain the Web API code. For those readers not interested in the server code (because they are not using ASP.NET), they can skip to the client section.

ASP.NET Web API

I am not going to go through the details on how to setup the ASP.NET Web API project. Interested readers can read the this tutorial and that tutorial provided by Microsoft.

The Web API is based loosely on MVC design. MVC stands for Model, View and Controller. Model represents the data layer, usually they are class that models after data design in storage, View represents the presentation layer while Controller is the business logic layer. Strictly speaking, a pure ASP.NET Web API server does not serve out HTML pages, so it does not have the presentation layer. In our example, we have the Product class as our data Model.

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Qty { get; set; }
    public decimal Price { get; set; }
}

In our ProductsController, Product is stored in a static Dictionary which is not persistent, meaning to say the data disappear after the Web API server shuts down. But this should suffice for our demostration without involving the use of database.

public class ProductsController : ApiController
{
    static Dictionary products = new Dictionary();

This is the Create method to create a Product object to store in our dictionary. [FromBody] attribute means the Product item shall be populated with the contents found in the request body.

[HttpPost]
public IHttpActionResult Create([FromBody] Product item)
{
    if (item == null)
    {
        return BadRequest();
    }

    products[item.Id] = item;

    return Ok();
}

To test our code, I use curl command. If you have already had Postman installed, you can use that as well. I am old school, so I prefer to use curl command directly.

curl -XPOST http://localhost:51654/api/products/create -H 'Content-Type: application/json' -d'{"Id":1, "Name":"ElectricFan","Qty":14,"Price":20.90}'
  • -X specifies the HTTP verb, POST which corresponds to create or update method.
  • 2nd argument is the URL to which this request should go to.
  • -H specifies the HTTP headers. We send JSON string so we set ‘Content-Type’ to ‘application/json’.
  • -d specifies the content body of the request. It can be seen clearly that the keys in the json dictionary correspond exactly to the Product members.

The output returned by curl is empty when the post request is successful. To see our created Product, we need to have the retrieval method which is discussed shortly below.

The methods to retrieve all products and a single product are listed below. Note: HTTP GET verb is used for data retrieval.

[HttpGet]
public List GetAll()
{
    List temp = new List();
    foreach(var item in products)
    {
        temp.Add(item.Value);
    }
    return temp;
}

[HttpGet]
public IHttpActionResult GetProduct(long id)
{
    try
    {
        return Ok(products[id]);
    }
    catch (System.Collections.Generic.KeyNotFoundException)
    {
        return NotFound();
    }
}

The respective curl commands retrieve all and a single Product based on id (which is 1). The commandline argument is similar to what I have explained in above, so I skip them.

curl -XGET http://localhost:51654/api/products'

curl -XGET http://localhost:51654/api/products/1'

The output is

[{"Id":1,"Name":"ElectricFan","Qty":14,"Price":20.90}]

{"Id":1,"Name":"ElectricFan","Qty":14,"Price":20.90}

We see the 1st output is enclosed by [] because 1st command returns a collection of Product objects but in our case, we only have 1 Product right now.

Lastly, we have the Update and Delete method. The difference between HTTP POST and PUT verb, is that PUT is purely a update method whereas POST create the object if it does not exist but POST can used for updating as well.

[HttpPut]
public IHttpActionResult Update(long id, [FromBody] Product item)
{
    if (item == null || item.Id != id)
    {
        return BadRequest();
    }

    if(products.ContainsKey(id)==false)
    {
        return NotFound();
    }
    var product = products[id];

    product.Name = item.Name;
    product.Qty = item.Qty;
    product.Price = item.Price;

    return Ok();
}
[HttpDelete]
public IHttpActionResult Delete(long id)
{
    var product = products[id];
    if (product == null)
    {
        return NotFound();
    }

    products.Remove(id);

    return Ok();
}

The respective curl commands below updates and deletes Product correspond to id=1.

curl -XPUT http://localhost:51654/api/products/1 -H 'Content-Type: application/json' -d'{"Id":1, "Name":"ElectricFan","Qty":15,"Price":29.80}'

curl -XDELETE http://localhost:51654/api/products/1

To see the Product is really updated or deleted, we have to use the retrieval curl command shown above.

C++ Client Code

At last, we have to come to main focus of this article! To able to use C++ Requests, please clone or download it at here and include its cpr header. Alternatively for Visual C++ users, you can install C++ Requests via vcpkg. C++ Requests is abbreviated as cpr in vcpkg.

.\vcpkg install cpr
#include <cpr/cpr.h>

To send a POST request to create a Product, we put our Product json in raw string literal inside the cpr::Body. Otherwise, without raw string literal, we have to escape all the double quotes found in our json string.

auto r = cpr::Post(cpr::Url{ "http://localhost:51654/api/products/create" },
    cpr::Body{ R"({"Id":1, "Name":"ElectricFan","Qty":14,"Price":20.90})" },
    cpr::Header{ { "Content-Type", "application/json" } });

Compare this C++ code to raw curl command, we can see which information goes to where.

curl -XPOST http://localhost:51654/api/products/create -H 'Content-Type: application/json' -d'{"Id":1, "Name":"ElectricFan","Qty":14,"Price":20.90}'

After product creation, we try to retrieve it using the C++ code below.

auto r = cpr::Get(cpr::Url{ "http://localhost:51654/api/products/1" });

The output is the same as the one from curl command which isn’t strange since C++ Requests utilize libcurl underneath to do its work.

{"Id":1,"Name":"ElectricFan","Qty":14,"Price":20.90}

The full C++ code to do CRUD with ASP.NET Web API is listed below with its output. By the way, CRUD is short for Create, Retrieve, Update and Delete. Be sure your ASP.NET Web API is up and running before running the C++ code below.

int main()
{
    {
        std::cout << "Action: Create Product with Id = 1" << std::endl;
        auto r = cpr::Post(cpr::Url{ "http://localhost:51654/api/products/create" },
            cpr::Body{ R"({"Id":1, "Name":"ElectricFan","Qty":14,"Price":20.90})" },
            cpr::Header{ { "Content-Type", "application/json" } });
        std::cout << "Returned Status:" << r.status_code << std::endl;
    }
    {
        std::cout << "Action: Retrieve the product with id = 1" << std::endl;
        auto r = cpr::Get(cpr::Url{ "http://localhost:51654/api/products/1" });
        std::cout << "Returned Text:" << r.text << std::endl;
    }
    {
        std::cout << "Action: Update Product with Id = 1" << std::endl;
        auto r = cpr::Post(cpr::Url{ "http://localhost:51654/api/products/1" },
            cpr::Body{ R"({"Id":1, "Name":"ElectricFan","Qty":15,"Price":29.80})" },
            cpr::Header{ { "Content-Type", "application/json" } });
        std::cout << "Returned Status:" << r.status_code << std::endl;
    }
    {
        std::cout << "Action: Retrieve all products" << std::endl;
        auto r = cpr::Get(cpr::Url{ "http://localhost:51654/api/products" });
        std::cout << "Returned Text:" << r.text << std::endl;
    }
    {
        std::cout << "Action: Delete the product with id = 1" << std::endl;
        auto r = cpr::Delete(cpr::Url{ "http://localhost:51654/api/products/1" });
        std::cout << "Returned Status:" << r.status_code << std::endl;
    }
    {
        std::cout << "Action: Retrieve all products" << std::endl;
        auto r = cpr::Get(cpr::Url{ "http://localhost:51654/api/products" });
        std::cout << "Returned Text:" << r.text << std::endl;
    }

    return 0;
}

The output as mentioned is shown below. I only display the returned text when the CRUD supports it, otherwise I just display the status. HTTP Status 200 means successful HTTP request. For example, Create/Update/Delete operation does not return any text, so I just display their status.

Action: Create Product with Id = 1
Returned Status:200

Action: Retrieve the product with id = 1
Returned Text:{"Id":1,"Name":"ElectricFan","Qty":14,"Price":20.90}

Action: Update Product with Id = 1
Returned Status:200

Action: Retrieve all products
Returned Text:[{"Id":1,"Name":"ElectricFan","Qty":15,"Price":29.80}]

Action: Delete the product with id = 1
Returned Status:200

Action: Retrieve all products
Returned Text:[]

For users looking to send request with parameters like below, you can make use of the cpr::Parameters.

http://www.example.com/products?quota=500&sold=true

C++ code for the above url example.

auto r = cpr::Get(cpr::Url{ "http://www.example.com/products" },
    cpr::Parameters{{"quota", "500"}, {"sold", "true"}});

Source code written for this article is hosted at Github.

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this:
search previous next tag category expand menu location phone mail time cart zoom edit close