Four years ago I wrote a blog post that shown how to build a web server using the http_listener from the C++ REST SDK library as well as a client application that consumed the exposed resources. Over the years there have been various changes to the API from the library and some readers complained the code no longer compiled. Eventually, I decided to revisit that post and update my code to the latest version of the library, which at this time is 2.10.
I will not reiterate all the details described in the former article. However, in summary, the server maintains a dictionary of values (both keys and values are strings). Through HTTP calls a client can retrieve the content of the dictionary, add new values, update or delete existing ones.
HTTP method | Description | Request | Response |
---|---|---|---|
GET | retrieves all the key-value pair from the dictionary | {"one" : "100", "two" : "200"} | |
POST | retrieves the values of the specified keys from the dictionary | ["one", "two", "three"] | {"one" : "100", "three" : " |
PUT | inserts new pairs of key-values in the dictionary; if a key is already found its value is updated | {"one" : "100", "two" : "200"} | {"one" : " |
DELETE | deletes the specified keys from the dictionary | ["one"] | {"one" : " |
Here is the server code:
#include <cpprest/http_listener.h> #include <cpprest/json.h> #pragma comment(lib, "cpprest_2_10") using namespace web; using namespace web::http; using namespace web::http::experimental::listener; #include <iostream> #include <map> #include <set> #include <string> using namespace std; #define TRACE(msg) wcout << msg #define TRACE_ACTION(a, k, v) wcout << a << L" (" << k << L", " << v << L")\n" map<utility::string_t, utility::string_t> dictionary; void display_json( json::value const & jvalue, utility::string_t const & prefix) { wcout << prefix << jvalue.serialize() << endl; } void handle_get(http_request request) { TRACE(L"\nhandle GET\n"); auto answer = json::value::object(); for (auto const & p : dictionary) { answer[p.first] = json::value::string(p.second); } display_json(json::value::null(), L"R: "); display_json(answer, L"S: "); request.reply(status_codes::OK, answer); } void handle_request( http_request request, function<void(json::value const &, json::value &)> action) { auto answer = json::value::object(); request .extract_json() .then([&answer, &action](pplx::task<json::value> task) { try { auto const & jvalue = task.get(); display_json(jvalue, L"R: "); if (!jvalue.is_null()) { action(jvalue, answer); } } catch (http_exception const & e) { wcout << e.what() << endl; } }) .wait(); display_json(answer, L"S: "); request.reply(status_codes::OK, answer); } void handle_post(http_request request) { TRACE("\nhandle POST\n"); handle_request( request, [](json::value const & jvalue, json::value & answer) { for (auto const & e : jvalue.as_array()) { if (e.is_string()) { auto key = e.as_string(); auto pos = dictionary.find(key); if (pos == dictionary.end()) { answer[key] = json::value::string(L"<nil>"); } else { answer[pos->first] = json::value::string(pos->second); } } } }); } void handle_put(http_request request) { TRACE("\nhandle PUT\n"); handle_request( request, [](json::value const & jvalue, json::value & answer) { for (auto const & e : jvalue.as_object()) { if (e.second.is_string()) { auto key = e.first; auto value = e.second.as_string(); if (dictionary.find(key) == dictionary.end()) { TRACE_ACTION(L"added", key, value); answer[key] = json::value::string(L"<put>"); } else { TRACE_ACTION(L"updated", key, value); answer[key] = json::value::string(L"<updated>"); } dictionary[key] = value; } } }); } void handle_del(http_request request) { TRACE("\nhandle DEL\n"); handle_request( request, [](json::value const & jvalue, json::value & answer) { set<utility::string_t> keys; for (auto const & e : jvalue.as_array()) { if (e.is_string()) { auto key = e.as_string(); auto pos = dictionary.find(key); if (pos == dictionary.end()) { answer[key] = json::value::string(L"<failed>"); } else { TRACE_ACTION(L"deleted", pos->first, pos->second); answer[key] = json::value::string(L"<deleted>"); keys.insert(key); } } } for (auto const & key : keys) dictionary.erase(key); }); } int main() { http_listener listener(L"http://localhost/restdemo"); 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]() {TRACE(L"\nstarting to listen\n"); }) .wait(); while (true); } catch (exception const & e) { wcout << e.what() << endl; } return 0; }
And this is the client code:
#include <cpprest/http_client.h> #include <cpprest/json.h> #pragma comment(lib, "cpprest_2_10") using namespace web; using namespace web::http; using namespace web::http::client; #include <iostream> using namespace std; void display_json( json::value const & jvalue, utility::string_t const & prefix) { wcout << prefix << jvalue.serialize() << endl; } pplx::task<http_response> make_task_request( http_client & client, method mtd, json::value const & jvalue) { return (mtd == methods::GET || mtd == methods::HEAD) ? client.request(mtd, L"/restdemo") : client.request(mtd, L"/restdemo", jvalue); } void make_request( http_client & client, method mtd, json::value const & jvalue) { make_task_request(client, mtd, jvalue) .then([](http_response response) { if (response.status_code() == status_codes::OK) { return response.extract_json(); } return pplx::task_from_result(json::value()); }) .then([](pplx::task<json::value> previousTask) { try { display_json(previousTask.get(), L"R: "); } catch (http_exception const & e) { wcout << e.what() << endl; } }) .wait(); } int main() { http_client client(U("http://localhost")); auto putvalue = json::value::object(); putvalue[L"one"] = json::value::string(L"100"); putvalue[L"two"] = json::value::string(L"200"); wcout << L"\nPUT (add values)\n"; display_json(putvalue, L"S: "); make_request(client, methods::PUT, putvalue); auto getvalue = json::value::array(); getvalue[0] = json::value::string(L"one"); getvalue[1] = json::value::string(L"two"); getvalue[2] = json::value::string(L"three"); wcout << L"\nPOST (get some values)\n"; display_json(getvalue, L"S: "); make_request(client, methods::POST, getvalue); auto delvalue = json::value::array(); delvalue[0] = json::value::string(L"one"); wcout << L"\nDELETE (delete values)\n"; display_json(delvalue, L"S: "); make_request(client, methods::DEL, delvalue); wcout << L"\nPOST (get some values)\n"; display_json(getvalue, L"S: "); make_request(client, methods::POST, getvalue); auto nullvalue = json::value::null(); wcout << L"\nGET (get all values)\n"; display_json(nullvalue, L"S: "); make_request(client, methods::GET, nullvalue); return 0; }
Notice there are slight changes in the way output is formatted, both in the server and the client application. The rest is mostly unchanged, except for the handling of JSON, that has changed significatly since version 1.1. Again, please see the original post for an explanation of the code.
The output from running these client and server applications is shown below. On the left is the client output, and on the right the server output.
Check out my C++ REST SDK Client Server app if you are interested
int main()
{
json::keep_object_element_order(true);
http_listener listener(L”http://localhost/restdemo”);
I would like to know how to call via a browser without using my client, http://localhost/restdemo?aaa=3
Neither the server nor the client works on ubuntu 16.04
g++ -std=c++11 server.cpp -o server -lboost_system -lcrypto -lssl -lcpprest -pthread
Hi,
I would like know how to create https server and client
God bless you man, seriously, you have no idea how much this helped me!
Hi Marius
I need to consume RESTful service written using CPPRest in a .NET client application. Is this supported?
It is not something cpprest needs to support. You can consume a eeb service using any programming language or tool that supports that. So, of course you can consume it from .Net.
Hi Marius, have you ever containerized an app using CPPRest in a Windows Container ? Take a look at this GitHub issue and let me know if you have the secret to make it work 🙂
https://github.com/Microsoft/cpprestsdk/issues/870
Thanks!
Just wanted to make note that wcout is required on Windows, since all strings are utf-16. If you’re on linux systems, cpprest uses utf-8 strings internally and you need to use cout instead.
hi, first of all thank you so much for your valuable blog! it has been really helping. it worked with my application just with minor changes. But still I have a doubt about receiving the response from the server….. how does the get function work with nullvalue???? what in case I want to receive another processed response from my application
Hi! First of all, thank you so much!
I have compiled your client code. When running it, the output says “Incorrect Content-Type: must be textual to extract_string, JSON to extract_json.”.Also, it shows the json, but no in the correct order.
Any ideas?
thanks
Anyone have an idea why I could receive GET and PUT messages but not DEL messages from http client from Chrome browser?
I know it’s CORS related somehow.
My server does this:
void WebAPIController::initRestOpHandlers() {
_listener.support(methods::GET, std::bind(&WebAPIController::handleGet, this, std::placeholders::_1));
_listener.support(methods::PUT, std::bind(&WebAPIController::handlePut, this, std::placeholders::_1));
_listener.support(methods::POST, std::bind(&WebAPIController::handlePost, this, std::placeholders::_1));
_listener.support(methods::DEL, std::bind(&WebAPIController::handleDelete, this, std::placeholders::_1));
_listener.support(methods::PATCH, std::bind(&WebAPIController::handlePatch, this, std::placeholders::_1));
}
when I put a breakpoint in the hander for the DEL request, it never get’s called.
the GET and PUT and POST for that matter, do.
any help is greatly appreciated.
Nick
Hi Marius,
Do you have some idea on how to check if in server side, the received file is a json or another extension ?
Like if we want to send an image or another media type trow this webservice ?
Thanks for your reply
Hii, I would like to know if anyone test this example sucessfully on ubuntu?? I get a lot of errors when compiling on ubuntu 16.04 and 18.04. I would appreciate any recomendation, thankss
with HTTPS, I get the error :
SSL Error: WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA SSL invalid CA. WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID SSL common name does not match. WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID SLL certificate is expired.
for the url of the format:
https://localhost:5234/appInfo
Could you please suggest a solution please
our server only listens to https
If we try http from client, we get the response:
WinHttpReceiveResponse: 12002: The operation timed out
If we try https from client, we get the response:
SSL Error: WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA SSL invalid CA. WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID SSL common name does not match. WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID SLL certificate is expired.
Please help.
Hello, I’d love to learn this library but I can’t get my hands on learning materials for writing the server code. Any help?
Hi,
I have installed cpprestsdk using vcpkg on windows 10 and built the c++ project using Visual Studio 2019. The program build without any errors.
But the program is not running and exiting with below error, when I run.
The thread 0x63c4 has exited with code 0 (0x0).
Exception thrown: write access violation.
this->**_Myproxy** was 0x700074.
anyone encountered similar problem?
If you put the effort into developing your own http server why would you waste your time using rest?, why not just develop your own services architecture?
thx,it work.
Hi Marius ,
Great post .
I want to listen more rest api on same ip and port . Do i need to create multiple http_listener or single listener fine . I want to consume api in C# console application . So i need to host multiple api on cpp which will be consume from other application .
Please suggest me , how to proceed .
Thanks in advance
Vikash
Hi,
Its a great post. Just a bit of more information I need. I want to convert a c++ object in json string. So that I can use this string to pass as input to post value to server.
Can any one help me with that.
Regards,
Amit
Is there a good example of a RESTFUL server with security SSL?
Hi, Does this code work on Visual studio 2019 ? because I’m seeing a crash in http_listener constructor itself ? Please guide me ?
Hi @Alexis I compiled dis code on Ubuntu, nd its successfully working. Can u please post the errors u r getting.
Mulțumesc frumos pentru exemple. Mi-a plăcut și cartea „Modern C++ Programming Cookbook”, pe care am cumpărat-o.