Прим. переводчика: в синтаксисе C++ напрочь отсутствуют несколько ограниченны средства построения предметно-ориентированных языков. В итоге их мало кто на С++ пытается использовать, а попытки всё-же это сделать вызывают интерес, тем более, когда в итоге получается нечто стройно выглядящее и практически полезное. Одним из таких открытий для меня стала библиотека Silicon, пытающаяся средствами современного С++ дать возможность быстро и гибко реализовать WebAPI в своём проекте. Давайте посмотрим, насколько просто это выглядит.
Hello World на Silicon — программа, которая на HTTP-запрос к
http://host/hello/world
ответит кодом 200 с текстом «hello world»:
auto my_api = http_api(GET / _hello / _world = [] () { return "hello world";}); mhd_json_serve(my_api, 80);
Неплохо, правда? my_api здесь это описание нашего API, а mhd_json_serve — это бекэнд библиотеки Silicon, реализующий данный API с использованием встроенного вебсервера (на выбор microhttpd или LWAN).
Давайте посмотрим, что ещё умеет Silicon.
Возвращаем JSON
GET / _hi = [] () { return D(_name = "John", _age = 42); }
Обработка параметров всех типов
POST / _hello / _id[int()] // URL-параметры * get_parameters(_name) // GET-параметры * post_parameters(_age = int()) // POST-параметры = [] (auto p) // p содержит три параметра { std::ostringstream ss; ss << p.name << p.age << p.id; return ss.str(); }
Опциональные параметры
GET / _hello * get_parameters(_id = optional(int(42)))
Связующий слой
Если вы пишете WebAPI, то с большой вероятностью вам может понадобиться доступ к базе данных. На Silicon это выглядит вот так:
auto my_api = http_api( GET / _username / _id[int()] = [] (auto p, mysql_connection& db) { std::string name; db("SELECT name from User where id = ?")(id) >> name; return D(_name = name); } ); auto middlewares = std::make_tuple( mysql_connection_factory("localhost", "user", "password", "database_name") ); mhd_json_serve(my_api, middlewares, 8080);
Поддерживаются MySQL и Sqlite.
Ошибки
Для возврата кодов ошибок HTTP-протокола используются исключения:
GET / _test / _id[int()] = [] (auto p) { if (p.id != 42) // Отправляет код 401 (Unauthorized) throw error::unauthorized("Wrong ID"); return "success"; }
Сессии
Мы, конечно же, можем помнить сессии пользователей (в базе данных или в памяти):
struct session { int id; }; auto api = http_api( GET / _set_id / _id[int()] = [] (auto p, session& s) { s.id = p.id; }, GET / _get_id = [] (session& s) { return D(_id = s.id); } ); auto middlewares = std::make_tuple( hashmap_session_factory<session>() ); mhd_json_serve(my_api, middlewares, 8080);
Тестирование созданного WebAPI
Мало толку от WebAPI, если все его методы не протестированы. К счастью, Silicon позволяет на основе описанного API получить клиент на базе libcurl_json_client, с уже готовыми функциями для вызова методов нашего API. Может использоваться как для тестирования, так и в реальном клиенте.
// Описываем API auto my_api = http_api( POST / _hello / _world / _id[int()] * get_parameters(_name, _city) = [] (auto p) { return D(_id = p.id, _name = p.name, _city = p.city); } ); // Запускаем сервер auto server = sl::mhd_json_serve(hello_api, 8080, _non_blocking); // Создаём клиент auto c = libcurl_json_client(my_api, "127.0.0.1", 8080); // c.http_get содержит GET-процедуры // c.http_post содержит POST-процедуры // c.http_put содержит PUT-процедуры // c.http_delete содержит DELETE-процедуры // Благодаря интроспекции клиент знает пути и параметры запроса auto r = c.http_post.hello.world(_id = 42, _name = "John", _city = "Paris"); assert(r.status == 200); assert(r.response.id == 42); assert(r.response.name == "John"); assert(r.response.city == "Paris");
ссылка на оригинал статьи https://habrahabr.ru/post/280214/
Добавить комментарий