Silicon Framework - WebAPI in C ++

Original author: Matthieu Garrigues
  • Transfer
  • Tutorial
Note translator: the C ++ syntax completely lacks a somewhat limited means of constructing domain-specific languages. As a result, few people in C ++ try to use them, but attempts to do this nevertheless are of interest, especially when the result is something that looks slim and practically useful. One of such discoveries for me was the Silicon framework , which is trying by means of modern C ++ to provide the ability to quickly and flexibly implement WebAPI in your project. Let's see how simple it looks.

Hello World on Silicon - a program that on an HTTP request to
http: // host / hello / world
will respond with code 200 with the text "hello world":
auto my_api = http_api(GET / _hello / _world  = [] () { return "hello world";});
mhd_json_serve(my_api, 80);


Not bad, right? here my_api is the description of our API, and mhd_json_serve is the backend of the Silicon framework that implements this API using the built-in web server (for choice microhttpd or LWAN ).

Let's see what else Silicon can do.

Return json
GET / _hi = [] () { return D(_name = "John", _age = 42); }


Processing parameters of all types
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();
   }


Optional parameters
GET / _hello * get_parameters(_id = optional(int(42)))


Link layer
If you write WebAPI, then with high probability you may need access to the database. On Silicon, it looks like this:

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 and Sqlite are supported.

Errors
The following exceptions are used to return HTTP protocol error codes:

GET / _test / _id[int()] = [] (auto p)
{
  if (p.id != 42)
    // Отправляет код 401 (Unauthorized)
    throw error::unauthorized("Wrong ID");
  return "success";
}


Sessions
We, of course, can remember user sessions (in the database or in memory):

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()
);
mhd_json_serve(my_api, middlewares, 8080);


Testing the Created WebAPI It is of
little use to the WebAPI if all of its methods are not tested. Fortunately, Silicon allows us to obtain a client based on libcurl_json_client based on the described API, with ready-made functions for calling methods of our API. It can be used both for testing and in the real client.

// Описываем 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");

Also popular now: