Добавляем Web API для программы на C++ с помощью библиотеки POCO

от автора

В жизни любой достаточно большой программы наступает момент, когда нужно вывести наружу какой-нибудь API — для плагинов, для интеграции с другими системами, для автоматизации и т.д. Для этого есть много разных технологий, но как-то так исторически сложилось, что сейчас принято делать API в виде REST-сервисов. В принципе, если не гнаться за экономией каждого байта и микросекунды, то в этом есть смысл: HTTP-запрос сделать легко из любого языка, это хорошо работает и локально, и по сети, не нужно сильно глубоко погружаться в недры сетевых протоколов.
Давайте посмотрим, как к уже существующей программе на 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

  1. Идём вот сюда и скачиваем последнюю версию Basic Edition for Windows (1.4.6p1 на момент написания этой статьи).
  2. Разархивируем куда-нибудь.
  3. Идём туда, куда разархивировали и видим там батники с именами build_vs90.cmd, build_vs100.cmd, build_vs110.cmd. Они собирают библиотеку POCO с помощью разных версий Visual Studio. Просто запустите нужный (студия, само-собой, к этому моменту должна быть уже установлена). Я лично компилировал POCO под VS2008, VS2010 и VS2012 — проблем не возникло ни разу.

Подключаем POCO к своему проекту

  1. Сделайте в папке своего проекта папку POCO
  2. В этой папке сделайте три подпапки: include, lib, bin.
  3. В папку include скопируйте все заголовочные файлы POCO (из папок %POCO%\XML\include, %POCO%\Util\include, %POCO%\Net\include и %POCO%\Foundation\include)
  4. В папку lib скопируйте lib-файлы из %POCO%\lib. Для удобства можно разнести их по подпапкам Debug и Release.
  5. В папку bin скопируйте dll-файлы из %POCO%\bin. Их аналогично можно разнести по подпапкам Debug и Release.
  6. Добавьте путь к папке include в Additional Include Directories, а путь к папке lib — в Additional Library Directories своего проекта. Удобно использовать переменную $(SolutionDir) и относительные пути.
  7. Добавьте файлы PocoFoundationd.lib, PocoNetd.lib, PocoUtild.lib в Additional Dependencies проекта для Debug-конфигурации, а их версии без буквы «d» в конце — для Release-конфигурации.
  8. Не забудьте, что dll-файлы из папки bin нужно будет деплоить вместе с вашим приложением. Можете просто их скопировать в папки Debug\Release или сделать шаг в процессе компиляции, где они будут туда копироваться.

В конце топика есть ссылка на настроенный проект на GitHub — можете использовать его как базу.

Используем POCO для создания Web API

Код получается очень простой и понятный. В главном файле создаём объект класса, унаследованного от ServerApplication. Это позволит нам сохранить в главном потоке вывод наших сообщений, одновременно запустив в другом потоке веб-сервер с помощью класса HTTPServer. Для того, чтобы объяснить веб-серверу, как обрабатывать входящие запросы мы сделаем свою фабрику, унаследованную от HTTPRequestHandlerFactory. На каждый запрос она будет создавать новый объект обработчика (класса, унаследованного от HTTPRequestHandler), который уже и будет заниматься разбором входящего запроса, его обработкой и выдачей клиенту результата.

В коде всё выглядит даже проще, чем в описании выше.

PocoHelloWorld.cpp

#include "MyRequestHandler.h"  int main(int argc, char** argv) { 	MyServerApp app; 	return app.run(argc, argv); } 

MyRequestHandler.h

#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; }; 

MyRequestHandler.cpp

#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/


Комментарии

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

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