LINUX.ORG.RU

RESTinio 0.6.6 с безопасным по типам роутером HTTP-запросов (на замену express-роутеру)

 , , , ,


1

4

RESTinio – это небольшая, открытая C++14 библиотека для встраивания асинхронного HTTP/Websocket сервера в C++ приложения. Распространяется под BSD-3-CLAUSE лицензией.

Намедни мы выкатили очередную версию 0.6.6, в которой, среди улучшений и исправлений, следует отметить две новых фичи.

Самая простая из них – это возможность повесить один обработчик сразу для нескольких методов при обращении к одному ресурсу. Например:

// Обработчик будет вызван для методов LOCK и UNLOCK.
router->add_handler(
   restinio::router::any_of_methods(
      restinio::http_method_lock(), restinio::http_method_unlock()),
   "/api/v1/resources/:rid",
   [](const auto & req, const auto & params) {...});

// Обработчик будет вызван для всех методов за исключением GET, POST и DELETE.
router->add_handler(
   restinio::router::none_of_methods(
      restinio::http_method_get(),
      restinio::http_method_post(),
      restinio::http_method_delete()),
   "/api/v1/users/:user",
   [](const auto & req, const auto & params) {...});

Но самое важное нововведение – это новый безопасный по типам роутер запросов, который может использоваться вместо привычного express-роутера. При этом новый роутер использует возможности C++ для выявления в compile-time ошибок, которые при использовании express-роутера проявляются только в run-time.

Новый роутер позволяет записать маршрут вот так:

namespace epr = restinio::router::easy_parser_router;
router->http_get(
   epr::path_to_params("/api/v1/posts/",
      epr::non_negative_decimal_number_p<std::uint64_t>(),
      "/revisions/",
      epr::non_negative_decimal_number_p<std::int16_t>()),
   [](const auto & req, std::uint64_t post_id, std::int16_t rev_id) {...});

тогда как в express-роутере этот же маршрут может описываться, например, так:

router->http_get("/api/v1/posts/:post_id(\d{1,10})/revisions/:rev_id(\d{1,5})",
   [](const auto & req, const auto & params) {
      const auto post_id = restinio::cast_to<std::uint64_t>(params["post_id"]);
      const auto rev_id = restinio::cast_to<std::int16_t>(params["rev_id"]);
   });

На Хабре можно найти статью, которая раскрывает тему нового роутера и некоторых деталей его реализации более подробно.

Взять и попробовать RESTinio можно с GitHub-а. Так же RESTinio доступен через Conan и vcpkg.

★★★★★

Ответ на: комментарий от xperious

У Boost.Beast другая цель. Это низкоуровневый конструктор из которого, при желании можно собрать все, что угодно. Но ручками придется пописать изрядно.

RESTinio же создавался для того, чтобы простые вещи делались проще и у разработчика было меньше головной боли. В частности, в RESTinio из коробки есть контроль за различными тайм-аутами. Что в других фреймворках, ЕМНИП, нужно было реализовывать самостоятельно.

Пару лет назад мы сделали что-то вроде сравнения выразительности. Переписали пример Винни Фалько, автора Beast-а, который он демонстрировал на CppCon 2018, с помощью RESTinio: https://github.com/Stiffstream/beast-cppcon2018-vs-restinio Желающие могут сами оценить что кому больше нравится.

Ну и можете просто на минимальный HelloWorld-сервер на RESTinio посмотреть:

#include <restinio/all.hpp>

int main()
{
	restinio::run(
		restinio::on_this_thread<>()
			.port(8080)
			.address("localhost")
			.request_handler([](auto req) {
				return req->create_response().set_body("Hello, World!").done();
			}));

	return 0;
}

Это все. Вред ли на Boost.Beast получится так же компактно без применения каких-либо высокоуровневых нашлепок.

Ну и раньше в Boost.Beast вроде бы роутера запросов вообще не было. Может сейчас добавился, не знаю.

eao197 ★★★★★ ()
Ответ на: комментарий от luntik2012

что-то не вижу в этом куске кода роутера запросов

Этот кусок кода демонстрирует количество усилий, необходимых для запуска минимального HTTP-сервера на RESTinio. Любой желающий может сравнить с таким же минимальным сервером на Boost.Beast.

Примеры с роутерами можно найти среди штатных примеров RESTinio. Например, для express-router и для easy_parser_router.

но как-то многовато геморроя

Если речь про это:

router->http_get(
   epr::path_to_params("/api/v1/posts/",
      epr::non_negative_decimal_number_p<std::uint64_t>(),
      "/revisions/",
      epr::non_negative_decimal_number_p<std::int16_t>()),
   [](const auto & req, std::uint64_t post_id, std::int16_t rev_id) {...});

То это лучшее, что удалось сделать при обеспечении безопасности по типам и статической типизации. Этот же маршрут в виде регулярок записывается компактнее, но там, по сути, динамическая типизация и никакого контроля со стороны компилятора.

Но, т.к. в RESTinio есть и тот и другой вид роутеров, то каждый может выбрать что ему удобнее.

eao197 ★★★★★ ()
Ответ на: комментарий от xperious

Так тема для анонимов закрыта, а регистрантам, полагаю, лень лишний раз со мной спорить :)

eao197 ★★★★★ ()
Ответ на: комментарий от eao197

Этот кусок кода демонстрирует количество усилий

Это понятно, как и следующее:

это лучшее, что удалось сделать при обеспечении безопасности по типам и статической типизации

Но тот факт, что у вас на пару строчек меньше кода, говорит только о том, что ваша либа среди нулей единица. Претензия не только лишь к вам, сколько к плюсам. Сам много лет пишу на плюсах, но конкретно rest серваки делаю на nim, гораздо приятнее имхо.

luntik2012 ()
Ответ на: комментарий от luntik2012

конкретно rest серваки делаю на nim

Такое ощущение, что вы эту тему вы зашли лишь для того, чтобы сказать именно это.

eao197 ★★★★★ ()
Ответ на: комментарий от imatveev13

Нет. В RESTinio поддерживается только сервер.

eao197 ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Любая тема о C/C++ заканчивается обсуждением нормальных языков =)

Подозреваю, что твои слова можно будет с тем же успехом применить и через 20 лет, потому что вряд ли какой другой язык программирования в обозримом будущем сможет подвинуть эту парочку с пьедестала, особенно учитывая, что все более разрастающийся кризис больнее всего ударит именно по маргинальным языкам. Последствия кризиса даже трудно предугадать.

Что касается обработки REST-запросов. А в С++ есть удобные генераторы для сериализации в джейсон и десериализации из него? Мне кажется, что это один из важнейших моментов для удобства написания сервисов на С++.

P.S. Мне особенно интересны сериализаторы в поток байт и десериализаторы из него. Есть ли такие для C++?

dave ★★★★★ ()
Ответ на: комментарий от dave

А в С++ есть удобные генераторы для сериализации в джейсон и десериализации из него?

Есть удобные фреймворки для работы с JSON по меркам C++. Но т.к. в C++ нет ни рефлексии, ни серьезных средств программирования в compile-time, то по сравнению с другими ЯП, они могут выглядеть… Ну недостаточно компактно, скажем так.

Например, есть https://github.com/nlohmann/json Один из наиболее раскрученных в мире C++.

Есть наша простая обертка вокруг RapidJSON: https://github.com/Stiffstream/json_dto Сочетает удобство nlohmann и скорость RapidJSON :)

eao197 ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Любая тема о C/C++ заканчивается обсуждением нормальных языков =)

А ты пробовал писать на Nim? Оно ж глючное и недоделанное.

Serral ()
Ответ на: комментарий от Serral

Он в каждую тему приходит гадить про Rust, так что не обращай внимание на него :)

zamazan4ik ★★ ()
Ответ на: комментарий от eao197

А бинарная сериализация, когда нужно перекинуть сообщение между узлами кластера с возможно разной big-little-endianness? Хотя я сейчас подумал, что это, возможно, редкий случай, и на него можно забить. Скорее всего, там можно будет зафигачить прямо двоичный образ POD-структуры. Но все же, тема интересна по-прежнему.

dave ★★★★★ ()
Ответ на: комментарий от dave

Есть Google Protobuf, Apache Thrift, Cap’n Proto.

Для каких-нибудь кастомных протоколов свои сериализаторы/десериализаторы пишут.

eao197 ★★★★★ ()
Ответ на: комментарий от RazrFalcon

По сравнению с C/C++ - норм.

Кому и кобыла невеста. (с)

Serral ()

RESTinio 0.6.7.1

Выпустили очередное обновление для RESTinio. В версии 0.6.7.1, кроме нескольких баг-фиксов, добавились:

Дополнительные опции для настройки CMake-сборок: RESTINIO_USE_EXTERNAL_EXPECTED_LITE, RESTINIO_USE_EXTERNAL_OPTIONAL_LITE, RESTINIO_USE_EXTERNAL_STRING_VIEW_LITE, RESTINIO_USE_EXTERNAL_VARIANT_LITE.

Вспомогательная функция run_async для упрощения запуска RESTinio-сервера на отдельном пуле потоков или отдельной рабочей нити:

int main() {
   auto server = restinio::run_async(
      // Задаем io_context для использования.
      // В данном случае у сервера будет собственный io_context.
      restinio::own_io_context(),
      // Параметры для HTTP-сервера.
      restinio::server_settings_t{}
         .address("127.0.0.1")
         .port(8080)
         .request_handler(...),
      // Размер пула рабочих нитей для HTTP-сервера.
      16);
   // Если оказались здесь и run_async не выбросил исключения,
   // значит HTTP-сервера запущен.

   ... // Какие-то другие действия.

   // Здесь нет необходимости останавливать HTTP-сервер
   // вручную, это будет сделано автоматически в
   // деструкторе переменной 'server'.
}

Добавлены новые вспомогательные функции для работа значений HTTP-заголовков. На этот раз заголовков Authorization и Proxy-Authorization.

Так же добавлены вспомогательные функции для извлечения из Authorization параметров для Basic и Bearer аутентификаций. Например, для Basic-аутентификации:

#include <restinio/all.hpp>
#include <restinio/http_field_parser/basic_auth.hpp>
...
auto on_request(const restinio::request_handle_t & req) {
   using namespace restinio::http_field_parsers::basic_auth;
   const auto auth_params = try_extract_params(*req,
         restinio::http_field::authorization);
   if(auth_params) { // Parameters successfully extracted.
      if(is_valid_user(auth_params->username, auth_params->password)) {
         ...
      }
   }
   ...
}
eao197 ★★★★★ ()

RESTinio 0.6.13

Вышел очередной релиз RESTinio, о котором можно рассказать, потому что в свежей версии 0.6.13 появилось парочка новых и важных фич.

Первая фича – это возможность провязать несколько обработчиков в цепочку. Какое-то приближение к middleware из EpressJS. При получении нового запроса обработчики из цепочки будут вызываться последовательно. Тем самым каждый обработчик может отвечать только за свою операцию (логирование параметров запроса, проверка значений в HTTP-заголовках, аутентификация и т.д.) и нужная приложению функциональность набирается посредством формирования соответствующей цепочки обработчиков.

Вторая фича, которая очень нужна для вышеупомянутых цепочек, – это возможность внедрить объект нужного пользователю типа в объект-запрос, который создается внутри RESTinio при получении нового входящего запроса. Такой объект может использоваться для передачи дополнительной информации от одной стадии обработки запроса к другой.

Более подробно все это описано в статье на Хабре. Там же изложены и некоторые соображения о том, куда RESTinio может двигаться дальше.

Так же хочется поблагодарить всех, кто проявил конструктивный интерес к RESTinio и, особенно, тех, кто рискнул и применил RESTinio у себя. Конечно же, отдельное спасибо тем, кто нашел возможность и время поделиться с нами своими впечатлениями, соображениями и обнаруженными недостатками.

eao197 ★★★★★ ()
Ограничение на отправку комментариев: только для зарегистрированных пользователей