Setting up
For Windows machines, clone vcpkg as follows.$ git clone https://github.com/Microsoft/vcpkg.git $ cd vcpkg
Then, open command window as admin and enter
> bootstrap-vcpkg.bat > vcpkg integrate install > vcpkg install cpprestsdk cpprestsdk:x64-windows
And for Debian based Linux machines, you can use apt to install cpprestsdk.
$ sudo apt install libcpprest-dev
On some systems, you might need to install the following packages before installing cpprestsdk.
$ sudo apt -y install libboost-atomic-dev libboost-thread-dev libboost-system-dev $ sudo apt -y install libboost-date-time-dev libboost-regex-dev $ sudo apt -y install libboost-filesystem-dev libboost-random-dev libboost-chrono-dev $ sudo apt -y libboost-serialization-dev libwebsocketpp-dev $ sudo apt -y install openssl libssl-dev ninja-build g++ git
If you want to build from source, you can build as follows.
$ unzip casablanca.zip $ cd casablanca $ mkdir -p build $ cd build $ cmake -G Ninja .. -DCMAKE_BUILD_TYPE=Release -DCPPREST_EXCLUDE_WEBSOCKETS=1 $ ninja $ sudo ninja install $ sudo ldconfig
Simple GET Client
Let us start building a simple program, getClient.cpp, which is listed below [2, 3]. See comments for code explanation.// File: getClient.cpp // Author: Yan Naing Aye #include <iostream> #include <cpprest/http_client.h> #include <cpprest/uri.h> #include <cpprest/json.h> using namespace utility; // Common utilities like string conversions using namespace web; // Common features like URIs. using namespace web::http; // Common HTTP functionality using namespace web::http::client; // HTTP client features using namespace concurrency::streams; // Asynchronous streams using namespace std; int main(int argc, char* argv[]) { // http client web::http::client::http_client client(U("https://yan9a.github.io")); // the pplx task 'resp' gives a placeholder for a value // that will be available in the future pplx::task<web::http::http_response> resp = client.request( web::http::methods::GET, U("/rpi/iot/cpprest/getClient/j1.json")); // attached a handler to be invoked when resp.is_done() is true pplx::task<json::value> jv = resp.then([=] (pplx::task<web::http::http_response> task) { web::http::http_response response = task.get(); // Check the status code. if (response.status_code() != 200) { throw std::runtime_error("Returned " + std::to_string(response.status_code())); } std::cout << "Returned " + std::to_string(response.status_code())<<endl; // Convert the response body to JSON object. pplx::task<json::value> jsonObject = response.extract_json(); return jsonObject; }); // handler to be invoked when json has been extracted jv.then([=](json::value jo){ cout<<"Val:"<<jo.serialize() << endl; }); // Wait until json value is ready try { jv.wait(); } catch (const std::exception & e) { printf("Error exception:%s\n", e.what()); } return 0; }
For Windows machines using Visual Studio, the library is automatically usable if you have already run "vcpkg integrate install" in the previous step. For Linux machines, make the CMakeLists.txt as follows.
cmake_minimum_required(VERSION 3.9) project(getClient) # for RPi # set ( CMAKE_PREFIX_PATH /usr/lib/arm-linux-gnueabihf/cmake/ ) # for x86_64 # set ( CMAKE_PREFIX_PATH /usr/lib/x86_64-linux-gnu/cmake/) # find_package(Boost COMPONENTS filesystem system REQUIRED) find_package(cpprestsdk REQUIRED) add_executable(getClient getClient.cpp) target_link_libraries(getClient cpprestsdk::cpprest # ${Boost_SYSTEM_LIBRARY} )
Then, you can make a build folder, build the program and run as shown below.
mkdir -p build cd build cmake .. make ./getClient
POST Client
A simple POST client postClient.cpp is shown below.// File: postClient.cpp // Author: Yan Naing Aye #include <iostream> #include <cpprest/http_client.h> #include <cpprest/filestream.h> #include <cpprest/uri.h> #include <cpprest/json.h> using namespace utility; // Common utilities like string conversions using namespace web; // Common features like URIs. using namespace web::http; // Common HTTP functionality using namespace web::http::client; // HTTP client features using namespace concurrency::streams; // Asynchronous streams using namespace std; int main(int argc, char* argv[]) { json::value jsonObject; jsonObject[U("apple")] = json::value::string(U("A")); jsonObject[U("banana")] = json::value::string(U("B")); // Make a POST request. auto requestJson = http_client(U("http://localhost:80")) .request(methods::POST, uri_builder(U("/j2.php")).to_string(), // auto requestJson = http_client(U("http://localhost:8080")) // .request(methods::POST, uri_builder(U("/cpprest/svrRest")).to_string(), jsonObject.serialize(), U("application/json")) // Get the response. .then([](http_response response) { // Check the status code. if (response.status_code() != 200) { throw std::runtime_error("Returned " + std::to_string(response.status_code())); } std::cout << "Returned " + std::to_string(response.status_code())<<endl; // Convert the response body to JSON object. return response.extract_json(); }) // Get the data field. .then([](json::value jsonObject) { cout<<"Val:"<<jsonObject.serialize() << endl; return jsonObject; }); // Wait for the concurrent tasks to finish. try { requestJson.wait(); } catch (const std::exception & e) { printf("Error exception:%s\n", e.what()); } return 0; }For the server side, we can create a simple PHP page, j2.php to serve the request from the POST client which is shown below.
<?php $json = file_get_contents('php://input'); // echo $json."\n"; $data = json_decode($json); $data->cherry = $data->apple.$data->banana; header('Content-Type: application/json'); echo json_encode($data); // curl -X POST "http://localhost/j2.php" // -H "accept: */*" -H "Content-Type: application/json" // -d "{\"apple\":\"A\",\"banana\":\"B\"}" ?>
After putting j2.php in the home directory of the web server, you can test it with the following command.
curl -X POST "http://localhost/j2.php" \ -H "accept: */*" -H "Content-Type: application/json" \ -d "{\"apple\":\"A\",\"banana\":\"B\"}"
As illustrated in the following figure, you will get the reply with added data in the JSON object.
Now, we can use the following CMakeLists.txt, build, and run the postClient program.
cmake_minimum_required(VERSION 3.9) project(postClient) # for RPi # set ( CMAKE_PREFIX_PATH /usr/lib/arm-linux-gnueabihf/cmake/ ) # for x86_64 # set ( CMAKE_PREFIX_PATH /usr/lib/x86_64-linux-gnu/cmake/) # find_package(Boost COMPONENTS filesystem system REQUIRED) find_package(cpprestsdk REQUIRED) add_executable(postClient postClient.cpp) target_link_libraries(postClient cpprestsdk::cpprest # ${Boost_SYSTEM_LIBRARY} )
mkdir -p build cd build cmake .. make ./postClient
CppREST Server
Let us develop a cpprestsdk server side application that can handle four methods - GET, POST, PUT, and DELETE. A very simple example implementation for rest server is shown below [4].// File: svrRest.cpp // Author: Yan Naing Aye #include <iostream> #include <cpprest/http_listener.h> #include <cpprest/filestream.h> #include <cpprest/uri.h> #include <cpprest/json.h> using namespace utility; // Common utilities like string conversions using namespace web; // Common features like URIs. using namespace web::http; // Common HTTP functionality using namespace web::http::experimental::listener; // HTTP listener using namespace concurrency::streams; // Asynchronous streams using namespace std; void handle_get(http_request message){ cout<<"Handle get: "<<message.to_string()<<endl; json::value jsonObject; jsonObject[U("apple")] = json::value::string(U("A")); jsonObject[U("banana")] = json::value::string(U("B")); message.reply(status_codes::OK,jsonObject); } void handle_post(http_request message){ cout<<"Handle post: "<<message.to_string()<<endl; json::value jsonObject; try{ message.extract_json() .then([&jsonObject](json::value jo){ cout<<"Val:"<<jo.serialize() << endl; jsonObject = jo; }) .wait(); } catch (const std::exception & e) { printf("Error exception:%s\n", e.what()); } jsonObject[U("cherry")] = json::value::string(U("C")); message.reply(status_codes::OK,jsonObject); } void handle_put(http_request message){ cout<<"Handle post: "<<message.to_string()<<endl; string rep = U("PUT handled"); message.reply(status_codes::OK,rep); } void handle_del(http_request message){ cout<<"Handle delete: "<<message.to_string()<<endl; string rep = U("DELETE handled"); message.reply(status_codes::OK,rep); } int main(int argc, char* argv[]) { web::http::experimental::listener::http_listener listener(U("http://localhost:8080/cpprest/svrRest")); listener.support(methods::GET,handle_get); listener.support(methods::POST,handle_post); listener.support(methods::PUT,handle_put); listener.support(methods::DEL,handle_del); try{ listener.open() .then([&listener](){printf("\nStarting svrRest\n");}) .wait(); while(true); } catch (const std::exception & e) { printf("Error exception:%s\n", e.what()); } return 0; }
Similarly, we can use the following CMakeLists.txt, build, and run the svrRest.cpp.
cmake_minimum_required(VERSION 3.9) project(svrRest) # for RPi # set ( CMAKE_PREFIX_PATH /usr/lib/arm-linux-gnueabihf/cmake/ ) # for x86_64 # set ( CMAKE_PREFIX_PATH /usr/lib/x86_64-linux-gnu/cmake/) # find_package(Boost COMPONENTS filesystem system REQUIRED) find_package(cpprestsdk REQUIRED) add_executable(svrRest svrRest.cpp) target_link_libraries(svrRest cpprestsdk::cpprest # ${Boost_SYSTEM_LIBRARY} )
mkdir -p build cd build cmake .. make ./svrRest
We can use previous postClient.cpp by changing the url to test the server. Alternatively, we can use the following command to send post request to the server and check the response.
curl -X POST "http://localhost:8080/cpprest/svrRest" -H "accept: */*" \ -H "Content-Type: application/json" -d "{\"apple\":\"A\",\"banana\":\"B\"}"
Similarly, we can use getClient.cpp or the following command to test get request.
curl -X GET "http://localhost:8080/cpprest/svrRest" -H "accept: */*"
GUI Multithreaded Examples
In this section, I will demonstrate some examples for cpprestsdk using multithreading and GUI. Example GUI client program GUI_Client.cpp can be found athttps://github.com/yan9a/rpi/blob/master/iot/cpprest/GUI_Client/GUI_Client.cpp.
You can select the HTTP request method whether it is GET or POST using the dropdown list. There are also text input boxes to set host name, path and content to submit. It creates a separate thread for each request.
You can also find another example for multithreaded C++ REST server at
https://github.com/yan9a/rpi/blob/master/iot/cpprest/svrRestThread/svrRestThread.cpp.
It uses multiple threads for listeners, and communicates using thread safe events.
References
[1] cpprestsdk. C++ REST SDK. 2020.url: https://github.com/Microsoft/cpprestsdk.
[2] Atakan SARIOGLU. Quick Start Your REST Client with CppREST. 2019-Nov-08.
url: http://www.atakansarioglu.com/easy-quick-start-cplusplus-rest-client-example-cpprest-tutorial/.
[3] Alexander Karatarakis. Programming with Tasks. 2015-Nov-03.
url: https://github.com/Microsoft/cpprestsdk/wiki/Programming-with-Tasks.
[4] Meenapintu. Restweb. 2019-Mar-17.
url: https://github.com/Meenapintu/Restweb.
No comments:
Post a Comment
Comments are moderated and don't be surprised if your comment does not appear promptly.