
In the life of any large enough program, there comes a point when you need to bring out some API - for plugins, for integration with other systems, for automation, etc. There are many different technologies for this, but somehow it has historically developed that it is now customary to do API in the form of REST services. In principle, if you don’t try to save every byte and microsecond, then it makes sense: to make an HTTP request easily from any language, it works well locally and over the network, you don’t need to dive deep into the depths of network protocols.
Let's see how you can quickly tie the Web API to an existing C ++ program using the
POCO library.
Task
Suppose we have this program:
#include <iostream> #include <conio.h> int main(int argc, char** argv) { for(;;) { std::cout << "Hello world!" << std::endl; _getch(); } return 0; }
We want to give the opportunity from the outside to change the displayed text. To do this, the client will only need to make a request of the form:
http: // host: port / setText? text = NewText
We collect POCO
- Go here and download the latest version of Basic Edition for Windows (1.4.6p1 at the time of this writing).
- Unarchive somewhere.
- We go where we have unzipped and see there batch files with the names build_vs90.cmd , build_vs100.cmd , build_vs110.cmd . They build the POCO library using different versions of Visual Studio. Just run the desired one (the studio, of course, should be already installed by this moment). I personally compiled POCO for VS2008, VS2010 and VS2012 - there were no problems even once.
')
We connect POCO to your project
- Make a folder in your project folder POCO
- In this folder, make three subfolders: include, lib, bin.
- In the include folder, copy all the POCO header files (from the% POCO% \ XML \ include,% POCO% \ Util \ include,% POCO% \ Net \ include, and% POCO% \ Foundation \ include folders)
- In the lib folder, copy the lib files from% POCO% \ lib. For convenience, you can spread them across the Debug and Release subfolders.
- Copy the dll files from% POCO% \ bin to the bin folder. They can be similarly distributed to the subfolders Debug and Release.
- Add the path to the include folder to Additional Include Directories , and the path to the lib folder to the Additional Library Directories of your project. It is convenient to use the $ (SolutionDir) variable and relative paths.
- Add the PocoFoundationd.lib, PocoNetd.lib, PocoUtild.lib files to the Additional Dependencies project for the Debug configuration, and their versions without the letter “d” at the end for the Release configuration.
- Do not forget that the dll-files from the bin folder will need to be deployed along with your application. You can simply copy them to the Debug \ Release folders or take a step in the compilation process, where they will be copied there.
At the end of the topic there is a link to a customized project on GitHub - you can use it as a base.
We use POCO to create Web API
The code turns out very simple and clear. In the main file, we create a class object inherited from the ServerApplication. This will allow us to save the output of our messages in the main thread while simultaneously launching a web server in another thread using the HTTPServer class. In order to explain to the web server how to handle incoming requests, we will make our factory inherited from the HTTPRequestHandlerFactory. For each request, it will create a new handler object (of a class inherited from HTTPRequestHandler), which will already be engaged in parsing the incoming request, processing it, and returning the result to the client.
In the code, everything looks even simpler than in the description above.
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; }
Additional materials
Thank you all, good luck in using POCO.