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

в 6:53, , рубрики: api, c++, Программирование, метки: ,

Добавляем 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%XMLinclude, %POCO%Utilinclude, %POCO%Netinclude и %POCO%Foundationinclude)
  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 нужно будет деплоить вместе с вашим приложением. Можете просто их скопировать в папки DebugRelease или сделать шаг в процессе компиляции, где они будут туда копироваться.

В конце топика есть ссылка на настроенный проект на 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.

Автор: tangro

Источник

Поделиться

* - обязательные к заполнению поля