I'm looking at this code which implements a simple JSON server using casablanca. I have not found a better example so far. The author seems to use a different HTTP method for each API which could become a problem when the server is supposed to many APIs. How should this problem be solved ? Would it mean that I would need multiple http_listeners ?
#include <cpprest/http_listener.h>
#include <cpprest/json.h>
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;
typedef std::vector<std::pair<utility::string_t, json::value>> field_map;
/* handlers implementation */
void handle_get(http_request request)
{
TRACE(L"\nhandle GET\n");
field_map answer;
for (auto const & p : dictionary)
{
answer.push_back(make_pair(p.first, json::value(p.second)));
}
request.reply(status_codes::OK, json::value::object(answer));
}
void handle_request(http_request request,
function<void(json::value &, field_map &)> action)
{
field_map answer;
request
.extract_json()
.then([&answer, &action](pplx::task<json::value> task) {
try
{
auto & jvalue = task.get();
if (!jvalue.is_null())
{
action(jvalue, answer);
}
}
catch (http_exception const & e)
{
wcout << e.what() << endl;
}
})
.wait();
request.reply(status_codes::OK, json::value::object(answer));
}
void handle_post(http_request request)
{
TRACE("\nhandle POST\n");
handle_request(
request,
[](json::value & jvalue, field_map & 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.push_back(make_pair(key, json::value(L"<nil>")));
}
else
{
answer.push_back(make_pair(pos->first, json::value(pos->second)));
}
}
}
}
);
}
void handle_put(http_request request)
{
TRACE("\nhandle PUT\n");
handle_request(
request,
[](json::value & jvalue, field_map & 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.push_back(make_pair(key, json::value(L"<put>")));
}
else
{
TRACE_ACTION(L"updated", key, value);
answer.push_back(make_pair(key, json::value(L"<updated>")));
}
dictionary[key] = value;
}
}
}
);
}
void handle_del(http_request request)
{
TRACE("\nhandle DEL\n");
handle_request(
request,
[](json::value & jvalue, field_map & 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.push_back(make_pair(key, json::value(L"<failed>")));
}
else
{
TRACE_ACTION(L"deleted", pos->first, pos->second);
answer.push_back(make_pair(key, json::value(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;
}
Yes, you can have multiple http_listeners based on the URI. It's working great.