Давайте посмотрим, как к уже существующей программе на C++ можно быстренько прикрутить Web API, используя для этого библиотеку POCO.
Задача
Пусть у нас есть вот такая программа:
#include <iostream> #include <conio.h> int main(int argc, char** argv) { for(;;) { std::cout << "Hello world!" << std::endl; _getch(); } return 0; }
Мы хотим дать возможность извне менять выводимый текст. Для этого клиенту будет достаточно сделать запрос вида:
http://host:port/setText?text=NewText
Собираем POCO
- Идём вот сюда и скачиваем последнюю версию Basic Edition for Windows (1.4.6p1 на момент написания этой статьи).
- Разархивируем куда-нибудь.
- Идём туда, куда разархивировали и видим там батники с именами build_vs90.cmd, build_vs100.cmd, build_vs110.cmd. Они собирают библиотеку POCO с помощью разных версий Visual Studio. Просто запустите нужный (студия, само-собой, к этому моменту должна быть уже установлена). Я лично компилировал POCO под VS2008, VS2010 и VS2012 — проблем не возникло ни разу.
Подключаем POCO к своему проекту
- Сделайте в папке своего проекта папку POCO
- В этой папке сделайте три подпапки: include, lib, bin.
- В папку include скопируйте все заголовочные файлы POCO (из папок %POCO%\XML\include, %POCO%\Util\include, %POCO%\Net\include и %POCO%\Foundation\include)
- В папку lib скопируйте lib-файлы из %POCO%\lib. Для удобства можно разнести их по подпапкам Debug и Release.
- В папку bin скопируйте dll-файлы из %POCO%\bin. Их аналогично можно разнести по подпапкам Debug и Release.
- Добавьте путь к папке include в Additional Include Directories, а путь к папке lib — в Additional Library Directories своего проекта. Удобно использовать переменную $(SolutionDir) и относительные пути.
- Добавьте файлы PocoFoundationd.lib, PocoNetd.lib, PocoUtild.lib в Additional Dependencies проекта для Debug-конфигурации, а их версии без буквы «d» в конце — для Release-конфигурации.
- Не забудьте, что dll-файлы из папки bin нужно будет деплоить вместе с вашим приложением. Можете просто их скопировать в папки Debug\Release или сделать шаг в процессе компиляции, где они будут туда копироваться.
В конце топика есть ссылка на настроенный проект на GitHub — можете использовать его как базу.
Используем POCO для создания Web API
Код получается очень простой и понятный. В главном файле создаём объект класса, унаследованного от ServerApplication. Это позволит нам сохранить в главном потоке вывод наших сообщений, одновременно запустив в другом потоке веб-сервер с помощью класса HTTPServer. Для того, чтобы объяснить веб-серверу, как обрабатывать входящие запросы мы сделаем свою фабрику, унаследованную от HTTPRequestHandlerFactory. На каждый запрос она будет создавать новый объект обработчика (класса, унаследованного от HTTPRequestHandler), который уже и будет заниматься разбором входящего запроса, его обработкой и выдачей клиенту результата.
В коде всё выглядит даже проще, чем в описании выше.
#include "MyRequestHandler.h" int main(int argc, char** argv) { MyServerApp app; return app.run(argc, argv); }
#pragma once #include <Poco/Mutex.h> #include <Poco/Util/ServerApplication.h> using namespace Poco; using namespace Poco::Util; using namespace std; class MyServerApp : public ServerApplication { public: static string getText(); static void setText(string newText); protected: int main(const vector<string> &); static string text; static Mutex textLock; };
#include <iostream> #include <conio.h> #include <string> #include "MyRequestHandler.h" #include <Poco/Net/HTTPServerRequest.h> #include <Poco/Net/HTTPRequestHandler.h> #include <Poco/Net/HTTPServerResponse.h> #include <Poco/Net/HTTPServer.h> #include <Poco/Net/HTTPRequestHandlerFactory.h> #include <Poco/ScopedLock.h> #include <Poco/URI.h> #include <Poco/StringTokenizer.h> using namespace Poco::Net; string MyServerApp::text = "Hello world!"; Mutex MyServerApp::textLock; class CMyRequestHandler : public HTTPRequestHandler { public: void handleRequest(HTTPServerRequest &req, HTTPServerResponse &resp) { resp.setStatus(HTTPResponse::HTTP_OK); resp.setContentType("text/html"); ostream& out = resp.send(); URI uri(req.getURI()); if (uri.toString().find("/setText") == 0) { StringTokenizer str(uri.getQuery(), "="); if (str.count() == 2 && str[0] == "text") { MyServerApp::setText(str[1]); out << "ok"; out.flush(); return; } } out << "error"; out.flush(); } }; class MyRequestHandlerFactory : public HTTPRequestHandlerFactory { public: virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest &) { return new CMyRequestHandler; } }; void MyServerApp::setText(string newText) { ScopedLock<Mutex> lock(textLock); text = newText; } string MyServerApp::getText() { ScopedLock<Mutex> lock(textLock); return text; } int MyServerApp::main(const vector<string> &) { HTTPServer s(new MyRequestHandlerFactory, ServerSocket(8000), new HTTPServerParams); s.start(); for(;;) { cout << MyServerApp::getText() << endl; _getch(); } s.stop(); return Application::EXIT_OK; }
Дополнительные материалы
Всем спасибо, удачи в использовании POCO.
ссылка на оригинал статьи http://habrahabr.ru/post/178403/
Добавить комментарий