Библиотека Silicon — WebAPI на C++

от автора

Прим. переводчика: в синтаксисе 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *