Вступ
Як ефективно та потужно створювати RESTful-сервіси? Використовуючи можливості продуктивності C++. Ми навчимося налаштовувати сервер, обробляти HTTP-запити та парсити JSON за допомогою таких бібліотек, як Boost.Beast та nlohmann/json.
RESTful API на C++
Що таке RESTful API?
Це основа сучасної веб-розробки для стандартизованого та масштабованого взаємодії клієнтських додатків із серверами. REST розшифровується як REpresentational State Transfer, що в перекладі означає «передача стану представлення». Це архітектурний стиль, за допомогою якого для доступу до веб-ресурсів та роботи з ними використовується протокол передачі даних без збереження стану, зазвичай HTTP.
RESTful API є ефективними та зручними завдяки дотриманню ряду принципів та обмежень:
1.
Без збереження стану. У кожному запиті клієнта до сервера повинна бути вся інформація, необхідна для розуміння та обробки цього запиту. Оскільки між запитами сервер не зберігає жодного контексту клієнта, таке взаємодія є безстатевим.
-
Ідентифікація ресурсів. Ресурси ідентифікуються за допомогою URL-адрес, тобто єдинообразних вказівників їхнього місцезнаходження. Кожен ресурс має унікальну URL-адресу, що спрощує доступ до ресурсу та роботу з ним.
-
Єдиний інтерфейс. У RESTful API операції над ресурсами виконуються стандартними HTTP-методами: GET, POST, PUT, DELETE, PATCH та іншими. Завдяки такій однорідності спрощується проектування та розуміння API.
-
Представлення ресурсів. Ресурси представлені в різних форматах: JSON, XML або простий текст. Найпоширеніший, простий і зручний формат — це JSON, або нотація об'єктів JavaScript.
5.
Взаємодія без збереження стану. Щоб запит клієнта був виконаний сервером, він повинен містити всю необхідну інформацію. Це забезпечує незалежну обробку кожного запиту.
Переваги C++ для RESTful API
C++ славиться своєю продуктивністю та ефективністю, що робить його відмінним вибором для створення високопродуктивних RESTful API. Ось чому:
- Продуктивність. C++ забезпечує низькорівневий доступ до пам'яті та системних ресурсів, що дозволяє оптимізувати продуктивність. Це особливо корисно для API, яким потрібна висока пропускна здатність та низька затримка.
- Контроль. Завдяки детальному контролю над системними ресурсами та пам'яттю, розробники можуть створювати високоефективні програми.
- Конкурентність. C++ підтримує багатопоточність та конкурентність, що є важливими для ефективної обробки одночасних запитів до API.
4.
Серйозні бібліотеки. В екосистемі C++ є потужні бібліотеки, такі як Boost.Beast для обміну даними по HTTP та веб-сокетах і nlohmann/json для парсингу JSON. Ці бібліотеки спрощують процес розробки та розширюють можливості API. - Платформонезалежна розробка. На C++ RESTful API інтерфейси створюються та розгортаються на Windows, Linux та macOS.
Ключові компоненти RESTful API
Розглянемо основні компоненти для створення RESTful API на C++:
- HTTP-сервер. Він обробляє вхідні HTTP-запити та відправляє відповідні відповіді. З бібліотекою Boost.Beast створення HTTP-серверів спрощується.
- Маршрутизація. Визначає, як обробляти різні HTTP-запити, співвідносить URL-адреси з конкретними функціями або методами, якими ці запити обробляються.
- Обробка запитів. Обробники запитів займаються обробкою вхідних запитів, виконують необхідні операції та генерують відповідні відповіді.
Зазвичай це відбувається при взаємодії з базою даних або іншими серверними службами. - Генерація відповідей. Відповіді генеруються на основі результату обробки запиту. При цьому задаються код стану HTTP, заголовки та вміст тіла відповіді.
- Парсинг JSON. JSON — поширений формат даних для взаємодії з API. Парсинг та генерування даних у форматі JSON необхідні для обробки запитів і відповідей. Для цієї мети зручно використовувати бібліотеку nlohmann/json.
Налаштування середовища розробки
Перед тим, як приступити до реалізації, налаштуємо середовище розробки та встановимо:
- Сучасний компілятор C++: GCC, Clang або MSVC.
- Генератор систем зборки CMake, який спрощує процес зборки.
- Бібліотеки Boost, колекцію рецензованих платформонезалежних бібліотек з вихідним кодом на C++. Завантажуємо звідси.
4.
Популярну JSON-бібліотеку nlohmann/json для C++ завантажуємо з репозиторію на GitHub або підключаємо через єдиний заголовочний файл.
Після встановлення необхідних інструментів і бібліотек, приступимо до створення RESTful API.
Приклад додатка
Демонструємо процес створення RESTful API на C++ на прикладі простого додатка з базовими CRUD-операціями — створення, читання, зміна, видалення — для керування колекцією даних. В нього входять:
- Ініціалізація сервера: налаштування HTTP-сервера з Boost.Beast.
- Маршрутизація: визначення маршрутів для кінцевих точок API.
- Обробка запитів: реалізація обробників для HTTP-методів: GET, POST, PUT, DELETE.
4.
Парсинг JSON: використання nlohmann/json для аналізу та генерації даних у форматі JSON.
До кінця статті у вас повинно сформуватися фундаментальне розуміння того, як створювати RESTful API на C++, розширювати і налаштовувати додаток під ваші завдання.
Налаштування сервера з Boost.Beast
Boost.Beast
Boost.Beast — бібліотека C++ для обміну даними через HTTP та веб-сокети, основою якої є Boost.Asio. Це потужний, гнучкий спосіб створення мережевих додатків, чудовий вибір для створення RESTful API.
З Boost.Beast багато в роботі з протоколами HTTP спрощується, а розробники можуть зосередитися на реалізації логіки додатків.
Налаштування проєкту
Перш ніж зануритись у код, налаштуємо новий проєкт на C++.
Створюємо для нього каталог, а для управління процесом збірки та включення необхідних залежностей налаштовуємо такий файл CMakeLists.txt
:
cmake_minimum_required(VERSION 3.10)
project(RestfulApi)
set(CMAKE_CXX_STANDARD 17)
find_package(Boost REQUIRED COMPONENTS system filesystem)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(RestfulApi main.cpp)
target_link_libraries(RestfulApi ${Boost_LIBRARIES})
У цій конфігурації вказується необхідна для проєкту версія CMake 3.10 або новіша та використовуваний стандарт C++17, а також знаходяться та прив'язуються до проєкту необхідні компоненти Boost system
та filesystem
.
Створення HTTP-сервера
Щоб створити простий HTTP-сервер за допомогою Boost.Beast, який буде прослуховувати вхідні з'єднання на вказаному порті та отримувати базове відповідне повідомлення, створюємо файл main.cpp
і додаємо такий код:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace beast = boost::beast; // з «»
namespace http = beast::http; // з «»
namespace net = boost::asio; // з «»
using tcp = net::ip::tcp; // з «»
// Ця функція генерує HTTP-відповідь на запит.
http::response handle_request(http::request const& req) {
// Відповідаємо на запит «GET» повідомленням «"Hello, World!"»
if (req.method() == http::verb::get) {
http::response res{http::status::ok, req.version()};
res.set(http::field::server, "Beast");
res.set(http::field::content_type, "text/plain");
res.keep_alive(req.keep_alive());
res.body() = "Hello, World!";
res.prepare_payload();
return res;
}
// Відповідь за замовчуванням для непідтримуваних методів
return http::response{http::status::bad_request, req.version()};
}
// Цей клас обробляє підключення HTTP-сервера.
class Session : public std::enable_shared_from_this<Session> {
tcp::socket socket_;
beast::flat_buffer buffer_;
http::request req_;
public:
explicit Session(tcp::socket socket) : socket_(std::move(socket)) {}
void run() {
do_read();
}
private:
void do_read() {
auto self(shared_from_this());
http::async_read(socket_, buffer_, req_, [this, self](beast::error_code ec, std::size_t) {
if (!ec) {
do_write(handle_request(req_));
}
});
}
void do_write(http::response res) {
auto self(shared_from_this());
auto sp = std::make_shared<http::response>(std::move(res));
http::async_write(socket_, *sp, [this, self, sp](beast::error_code ec, std::size_t) {
socket_.shutdown(tcp::socket::shutdown_send, ec);
});
}
};
// Цей клас обробляє вхідні підключення та запускає сеанси.
class Listener : public std::enable_shared_from_this<Listener> {
net::io_context& ioc_;
tcp::acceptor acceptor_;
public:
Listener(net::io_context& ioc, tcp::endpoint endpoint)
: ioc_(ioc), acceptor_(net::make_strand(ioc)) {
beast::error_code ec;
// Відкриваємо приймач
acceptor_.open(endpoint.protocol(), ec);
if (ec) {
std::cerr << "Помилка відкриття: " << ec.message() << std::endl;
return;
}
// Дозволяємо повторне використання адреси
acceptor_.set_option(net::socket_base::reuse_address(true), ec);
if (ec) {
std::cerr << "Помилка налаштування опції: " << ec.message() << std::endl;
return;
}
// Прив'язуємо до адреси сервера
acceptor_.bind(endpoint, ec);
if (ec) {
std::cerr << "Помилка прив'язки: " << ec.message() << std::endl;
return;
}
// Починаємо прослуховування підключень
acceptor_.listen(net::socket_base::max_listen_connections, ec);
if (ec) {
std::cerr << "Помилка прослуховування: " << ec.message() << std::endl;
return;
}
do_accept();
}
private:
void do_accept() {
acceptor_.async_accept(net::make_strand(ioc_), [this](beast::error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared<Session>(std::move(socket))->run();
}
do_accept();
});
}
};
int main() {
try {
auto const address = net::ip::make_address("0.0.0.0");
unsigned short port = 8080;
net::io_context ioc{1};
std::make_shared<Listener>(ioc, tcp::endpoint{address, port})->run();
ioc.run();
} catch (const std::exception& e) {
std::cerr << "Помилка: " << e.what() << std::endl;
}
}
Розглянемо роль кожного компонента коду у створенні базового HTTP-сервера з Boost.Beast.
Простори імен та псевдоніми типів:
- Для спрощення коду та підвищення зручності його сприйняття визначаємо простори імен та псевдоніми типів.
- У
namespace beast = boost::beast;
асоціюємоboost::beast
зbeast
, що спрощує звернення до функцій та класів, пов'язаних з Beast. - У
namespace http = beast::http;
асоціюємоbeast::http
зhttp
, що дає змогу використовувати HTTP-функціональність бібліотеки Boost.Beast. - У
namespace net = boost::asio;
асоціюємоboost::asio
зnet
, таким чином отримуючи доступ до мережевих компонентів Boost.Asio. - За допомогою
using tcp = net::ip::tcp;
створюється псевдонім типуtcp
дляboost::asio::ip::tcp
, який застосовуємо для мережевих операцій TCP.
Обробник HTTP-запитів:
- Функцією
handle_request
обробляються вхідні HTTP-запити і генеруються відповіді. - Як параметр приймається об'єкт
http::request
, тобто HTTP-запит. - Функція перевіряє HTTP-метод запиту.
Якщо це GET-запит, створюється HTTP-відповідь з кодом стану 200 OK, в типі вмісту задається text/plain
, а в тілі — “Hello, World!”.
- Потім відповідь повертається, готова до відправлення клієнту.
http::response handle_request(http::request const& req) {
if (req.method() == http::verb::get) {
http::response res{http::status::ok, req.version()};
res.set(http::field::server, "Beast");
res.set(http::field::content_type, "text/plain");
res.keep_alive(req.keep_alive());
res.body() = "Hello, World!";
res.prepare_payload();
return res;
}
return http::response{http::status::bad_request, req.version()};
}
Клас Session:
- Класом
Session
керується окремими клієнтськими підключеннями. - В ньому містяться TCP-сокет
tcp::socket
та буферbeast::flat_buffer
для читання даних. - Процес зчитування ініціюється в методі
run
викликомdo_read
. - У
do_read
за допомогоюhttp::async_read
асинхронно зчитується HTTP-запит з клієнта.
Викликом handle_request
зчитаний запит обробляється, а потім за допомогою do_write
відправляється.
- З do_write
HTTP-відповідь через http::async_write
надсилається назад клієнту, після чого сокет закривається.
class Session : public std::enable_shared_from_this {
tcp::socket socket_;
beast::flat_buffer buffer_;
http::request req_;
public:
explicit Session(tcp::socket socket) : socket_(std::move(socket)) {}
void run() {
do_read();
}
private:
void do_read() {
auto self(shared_from_this());
http::async_read(socket_, buffer_, req_, [this, self](beast::error_code ec, std::size_t) {
if (!ec) {
do_write(handle_request(req_));
}
});
}
void do_write(http::response res) {
auto self(shared_from_this());
auto sp = std::make_shared>(std::move(res));
http::async_write(socket_, *sp, [this, self, sp](beast::error_code ec, std::size_t) {
socket_.shutdown(tcp::socket::shutdown_send, ec);
});
}
}
Клас Listener:
- Класом
Listener
на вказаній кінцевій точці приймаються вхідні підключення. - В ньому містяться контекст введення/виведення
net::io_context
та TCP-приймачtcp::acceptor
. - Конструктором приймач ініціалізується та відкривається, задається параметр повторного використання адреси, приймач прив'язується до кінцевої точки, і починається прослуховування підключень.
- Методом
do_accept
за допомогоюacceptor_.async_accept
асинхронно приймаються вхідні підключення.
Прийняте нове підключення обробляється щойно створеним об'єктомSession
, після чого для прийняття інших підключень знову викликаєтьсяdo_accept
.
class Listener : public std::enable_shared_from_this {
net::io_context& ioc_;
tcp::acceptor acceptor_;
public:
Listener(net::io_context& ioc, tcp::endpoint endpoint)
: ioc_(ioc), acceptor_(net::make_strand(ioc)) {
beast::error_code ec;
acceptor_.open(endpoint.protocol(), ec);
if (ec) {
std::cerr << "Open error: " << ec.message() << std::endl;
return;
}
acceptor_.set_option(net::socket_base::reuse_address(true), ec);
if (ec) {
std::cerr << "Set option error: " << ec.message() << std::endl;
return;
}
acceptor_.bind(endpoint, ec);
if (ec) {
std::cerr << "Bind error: " << ec.message() << std::endl;
return;
}
acceptor_.listen(net::socket_base::max_listen_connections, ec);
if (ec) {
std::cerr << "Listen error: " << ec.message() << std::endl;
return;
}
do_accept();
}
private:
void do_accept() {
acceptor_.async_accept(net::make_strand(ioc_), [this](beast::error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared(std::move(socket))->run();
}
do_accept();
});
}
};
Функція main:
- Функцією
main
ініціалізується сервер і запускається контекст вводу/виводу. - Створюються об'єкт контексту вводу/виводу
net::io_context ioc{1}
та об'єктListener
, який прив'язується до адреси0.0.0.0
та порту8080
. - Об'єкт
Listener
починає прийом підключень, а за допомогоюioc.run()
запускається контекст вводу/виводу, його виконання та обробка підключень продовжиться, поки робота сервера не припиниться.
int main() {
try {
auto const address = net::ip::make_address("0.0.0.0");
unsigned short port = 8080;
net::io_context ioc{1};
std::make_shared(ioc, tcp::endpoint{address, port})->run();
ioc.run();
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
}
Запуск сервера
Щоб запустити сервер, скомпільовуємо проект за допомогою CMake та запускаємо отриманий виконуваний файл.
У терміналі переходимо в каталог проекту, а потім запускаємо такі команди:
mkdir build
cd build
cmake ..
make
./RestfulApi
Тестуємо сервер у браузері або за допомогою curl
:
curl http://localhost:8080
З сервера має надійти відповідь Hello, World!
Розширення сервера
Запустивши базовий HTTP-сервер, доповнимо його обробкою HTTP-методів і парсингом корисних навантажень у форматі JSON. Далі розглянемо, як з обробкою HTTP-запитів та даними у форматі JSON справляється бібліотека nlohmann/json.
Обробка HTTP-запитів та відповідей
Для функціонального RESTful API важлива ефективна обробка різних HTTP-методів та корисного навантаження у форматі JSON.
Розширимо можливості базового сервера підтримкою HTTP-методів GET, POST, PUT, DELETE і скористаємося бібліотекою nlohmann/json для парсингу JSON.
Додавання підтримки JSON
Спочатку завантажуємо бібліотеку nlohmann/json з репозиторію GitHub або підключаємо її до проекту безпосередньо єдиним заголовковим файлом.
Обробка запитів JSON
Розпочнемо з оновлення функції handle_request
для обробки різних HTTP-методів і парсингу корисного навантаження JSON.
- Включення nlohmann/json: додаємо на початку файлу
main.cpp
таку директиву include:
http::response handle_request(http::request const& req) {
if (req.method() == http::verb::get && req.target() == "/api/data") {
// Обробляємо запит «GET»
nlohmann::json json_response = {{"message", "This is a GET request"}};
http::response res{http::status::ok, req.version()};
res.set(http::field::server, "Beast");
res.set(http::field::content_type, "application/json");
res.keep_alive(req.keep_alive());
res.body() = json_response.dump();
res.prepare_payload();
return res;
} else if (req.method() == http::verb::post && req.target() == "/api/data") {
// Обробляємо запит «POST»
auto json_request = nlohmann::json::parse(req.body());
std::string response_message = "Received: " + json_request.dump();
nlohmann::json json_response = {{"message", response_message}};
http::response res{http::status::ok, req.version()};
res.set(http::field::server, "Beast");
res.set(http::field::content_type, "application/json");
res.keep_alive(req.keep_alive());
res.body() = json_response.dump();
res.prepare_payload();
return res;
} else if (req.method() == http::verb::put && req.target() == "/api/data") {
// Обробляємо запит «PUT»
auto json_request = nlohmann::json::parse(req.body());
std::string response_message = "Оновлено: " + json_request.dump();
nlohmann::json json_response = {{"message", response_message}};
http::response res{http::status::ok, req.version()};
res.set(http::field::server, "Beast");
res.set(http::field::content_type, "application/json");
res.keep_alive(req.keep_alive());
res.body() = json_response.dump();
res.prepare_payload();
return res;
} else if (req.method() == http::verb::delete_ && req.target() == "/api/data") {
// Обробляємо запит «DELETE»
nlohmann::json json_response = {{"message", "Ресурс видалено"}};
http::response res{http::status::ok, req.version()};
res.set(http::field::server, "Beast");
res.set(http::field::content_type, "application/json");
res.keep_alive(req.keep_alive());
res.body() = json_response.dump();
res.prepare_payload();
return res;
}
// Відповідь за замовчуванням для непідтримуваних методів
return http::response{http::status::bad_request, req.version()};
}
У цій розширеній функції handle_request
:
- GET-запит: у відповіді повертається JSON-повідомлення з вказівкою на GET-запит.
- POST-запит: тіло запиту парситься у форматі JSON, створюється відповідне повідомлення і повертається як JSON.
- PUT-запит: подібно до POST-запиту, тіло парситься у форматі JSON, і повертається повідомлення з вказівкою, що ресурс було оновлено.
- DELETE-запит: повертається JSON-повідомлення з вказівкою, що ресурс було видалено.
Обробка HTTP-методів
Для обробки HTTP-методів розширюємо функцію handle_request
, щоб вона перевіряла тип кожного методу — GET, POST, PUT, DELETE — і відповідним чином обробляла запити.
Оновлений приклад коду повністю
Ось весь оновлений файл main.cpp
з новою функцією handle_request
та необхідними include:
#include
#include
cpp
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace beast = boost::beast; // з «»
namespace http = beast::http; // з «»
namespace net = boost::asio; // з «»
using tcp = net::ip::tcp; // з «»
// Ця функція повертає HTTP-відповідь на запит.
cpp
http::response handle_request(http::request const& req) {
if (req.method() == http::verb::get && req.target() == "/api/data") {
// Обробляємо запит «GET»
nlohmann::json json_response = {{"message", "This is a GET request"}};
http::response res{http::status::ok, req.version()};
res.set(http::field::server, "Beast");
res.set(http::field::content_type, "application/json");
res.keep_alive(req.keep_alive());
res.body() = json_response.dump();
res.prepare_payload();
return res;
} else if (req.method() == http::verb::post && req.target() == "/api/data") {
// Обробляємо запит «POST»
auto json_request = nlohmann::json::parse(req.body());
std::string response_message = "Received: " + json_request.dump();
nlohmann::json json_response = {{"message", response_message}};
http::response res{http::status::ok, req.version()};
res.set(http::field::server, "Beast");
res.set(http::field::content_type, "application/json");
res.keep_alive(req.keep_alive());
res.body() = json_response.dump();
res.prepare_payload();
return res;
} else if (req.method() == http::verb::put && req.target() == "/api/data") {
// Обробляємо запит «PUT»
auto json_request = nlohmann::json::parse(req.body());
std::string response_message = "Updated: " + json_request.dump();
nlohmann::json json_response = {{"message", response_message}};
http::response res{http::status::ok, req.version()};
res.set(http::field::server, "Beast");
res.set(http::field::content_type, "application/json");
res.keep_alive(req.keep_alive());
res.body() = json_response.dump();
res.prepare_payload();
return res;
} else if (req.method() == http::verb::delete_ && req.target() == "/api/data") {
// Обробляємо запит «DELETE»
nlohmann::json json_response = {{"message", "Resource deleted"}};
http::response res{http::status::ok, req.version()};
res.set(http::field::server, "Beast");
res.set(http::field::content_type, "application/json");
res.keep_alive(req.keep_alive());
res.body() = json_response.dump();
res.prepare_payload();
return res;
}
// Відповідь за замовчуванням для неподтримуваних методів
return http::response{http::status::bad_request, req.version()};
}
// Цим класом обробляється підключення HTTP-сервера.
cpp
class Session : public std::enable_shared_from_this<Session> {
tcp::socket socket_;
beast::flat_buffer buffer_;
http::request req_;
public:
explicit Session(tcp::socket socket) : socket_(std::move(socket)) {}
void run() {
do_read();
}
private:
void do_read() {
auto self(shared_from_this());
http::async_read(socket_, buffer_, req_, [this, self](beast::error_code ec, std::size_t) {
if (!ec) {
do_write(handle_request(req_));
}
});
}
void do_write(http::response res) {
auto self(shared_from_this());
auto sp = std::make_shared<http::response>(std::move(res));
http::async_write(socket_, *sp, [this, self, sp](beast::error_code ec, std::size_t) {
socket_.shutdown(tcp::socket::shutdown_send, ec);
});
}
};
// Цим класом приймаються вхідні підключення, запускаються сеанси.
cpp
class Listener : public std::enable_shared_from_this<Listener> {
net::io_context& ioc_;
tcp::acceptor acceptor_;
public:
Listener(net::io_context& ioc, tcp::endpoint endpoint)
: ioc_(ioc), acceptor_(net::make_strand(ioc)) {
beast::error_code ec;
// Відкриваємо приймач
acceptor_.open(endpoint.protocol(), ec);
if (ec) {
std::cerr << "Open error: " << ec.message() << std::endl;
return;
}
// Дозволяємо повторне використання адреси
acceptor_.set_option(net::socket_base::reuse_address(true), ec);
if (ec) {
std::cerr << "Set option error: " << ec.message() << std::endl;
return;
}
// Прив'язуємося до адреси сервера
acceptor_.bind(endpoint, ec);
if (ec) {
std::cerr << "Bind error: " << ec.message() << std::endl;
return;
}
// Починаємо прослуховування підключень
acceptor_.listen(net::socket_base::max_listen_connections, ec);
if (ec) {
std::cerr << "Listen error: " << ec.message() << std::endl;
return;
}
do_accept();
}
private:
void do_accept() {
acceptor_.async_accept(net::make_strand(ioc_), [this](beast::error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared<Session>(std::move(socket))->run();
}
do_accept();
});
}
};
int main() {
try {
auto const address = net::ip::make_address("0.0.0.0");
unsigned short port = 8080;
net::io_context ioc{1};
std::make_shared<Listener>(ioc, tcp::endpoint{address, port})->run();
ioc.run();
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
}
Пояснення розширеної функції handle_request
:
GET-запит:
- Перевіряється, чи є HTTP-метод GET, а цільова URL-адреса —
/api/data
. - Створюється відповідь у форматі JSON з повідомленням, яке вказує на те, що це GET-запит.
- В типі вмісту відповіді вказується
application/json
, і готується корисне навантаження.
POST-запит:
- Перевіряється, чи є HTTP-метод POST, а цільова URL-адреса —
/api/data
. - Розбирається тіло запиту у форматі JSON за допомогою
nlohmann::json::parse
. - Створюється відповідне повідомлення з вказівкою отриманих даних JSON.
- Готується відповідь у форматі JSON, в типі вмісту вказується
application/json
.
PUT-запит:
- Перевіряється, чи є HTTP-метод PUT, а цільова URL-адреса —
/api/data
. - Розбирається тіло запиту у форматі JSON.
- Створюється відповідне повідомлення з вказівкою оновлених даних JSON.
- Готується відповідь у форматі JSON, в типі вмісту вказується
application/json
.
DELETE-запит:
- Перевіряється, чи є HTTP-метод DELETE, а цільова URL-адреса —
/api/data
. - Створюється відповідь у форматі JSON з повідомленням, яке вказує на те, що ресурс видалено.
- В типі вмісту відповіді вказується
application/json
, і готується корисне навантаження.
Непідтримувані методи:
- Для будь-яких непідтримуваних HTTP-методів або URL-адресів повертається відповідь на некоректний запит.
Завдяки цій розширеній функціональності сервер тепер справляється з базовими CRUD-операціями за допомогою різних HTTP-методів і даних у форматі JSON.
Так формується основа RESTful API на C++.
Заключення
Створення RESTful API за допомогою C++ — це потужний спосіб розробки високопродуктивних веб-сервісів. Завдяки таким бібліотекам, як Boost.Beast для обміну даними через HTTP та nlohmann/json для парсингу JSON, можна створювати надійні та масштабовані API.
Ми розглянули основи налаштування сервера, обробки HTTP-запитів та взаємодії з даними JSON.
Застосовуючи ці інструменти та прийоми роботи, ви зможете створити та розширити власні RESTful API на C++, забезпечуючи їх ефективність та супроводжуваність.
Документація
- Boost.Beast.
- Справочник по C++.
- Офіційна документація по CMake.
Читайте також:
- Чому розробка веб-інтерфейсів така складна?
- JMeter-тестування динамічної навантаженості Restful API
- Налагодження API Java Message Service з використанням Lightrun
Читайте нас у Telegram, VK та Дзен
Переклад статті Alexander Obregon: Будування RESTful API за допомогою C++
Перекладено з: Создание RESTful API-интерфейсов на C++