├── .htaccess ├── README.md ├── connector.cpp ├── delete.cpp ├── get.cpp ├── post.cpp └── put.cpp /.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine on 2 | RewriteRule comic/([0-9]+)$ comic/get.fcgi?id=$1 3 | RewriteRule comic/put comic/put.fcgi 4 | RewriteRule comic/delete comic/delete.fcgi 5 | RewriteRule comic/post comic/post.fcgi 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpp-rest-api 2 | RESTFul CRUD Service Example by C++ 3 | 4 | ## Before You Begin 5 | 6 | Before you begin we recommend you read about the basic building blocks that assemble for this application. I’m assuming that you are using Ubuntu/Linux OS 7 | 8 | * [Apache2](http://www.apache.org/) 9 | * [MySQL](https://www.mysql.com/) 10 | * [FastCGI++](http://www.nongnu.org/fastcgipp/) 11 | * [mod_fastcgi](http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html) 12 | * [mod_fcgid](http://httpd.apache.org/mod_fcgid/) 13 | * [Boost](http://www.boost.org/) 14 | 15 | First step is to install below mods on Apache2 to enable support for FastCGI: 16 | 17 | ``` 18 | sudo apt-get install libapache2-mod-fastcgi libapache2-mod-fcgid 19 | ``` 20 | 21 | Once installed, restart the Apache2 service by calling sudo service apache2 restart. 22 | 23 | Now it’s necessary to download and install [Boost](http://www.boost.org/). You can do it here. Download it from the official website, unpack it and call the following commands on terminal: 24 | ``` 25 | ./boostrap 26 | sudo ./b2 install 27 | ``` 28 | And finally, download [FastCGI++](http://www.nongnu.org/fastcgipp/) through the following link: http://www.nongnu.org/fastcgipp/ 29 | 30 | The procedure to install it is the default one: 31 | 32 | ``` 33 | ./configure 34 | make 35 | sudo make install 36 | ``` 37 | 38 | Finally, install MySQL, Initialize MySQL into terminal , Create Database name `book_shop` && select database : 39 | ``` 40 | sudo apt-get install MySQL-server 41 | mysql -u USERNAME -p 42 | CREATE DATABASE book_shop; 43 | USE book_shop; 44 | ``` 45 | 46 | And finally let’s create a table for `Book`: 47 | 48 | ``` 49 | CREATE TABLE Book( 50 | id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, 51 | name VARCHAR(30) NOT NULL, 52 | publisher VARCHAR(30) NOT NULL, 53 | date TIMESTAMP, 54 | edition INT 55 | ); 56 | ``` 57 | Now compile cpp files using : 58 | 59 | ``` 60 | sudo g++ *.cpp -I/usr/local/include/mysqlcppconn/ -lfastcgipp -lboost_date_time -lboost_system -lboost_thread -lmysqlcppconn -o post.fcgi 61 | ``` 62 | >You must put the “.fcgi” extension in order to access the page. A more elegant solution would be, instead of GET /books/book.fcgi?id=10, the following: GET /books/10. 63 | 64 | It’s shorter and now the user don’t need to know we are using a FCGI script. URL rewriting is completely possible on Apache Web Server. You just need to do the following: 65 | 66 | ``` 67 | sudo a2enmod rewrite 68 | sudo service apache2 restart 69 | ``` 70 | -------------------------------------------------------------------------------- /connector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class Connector : public Fastcgipp::Request 16 | { 17 | public: 18 | Connector() 19 | { 20 | driver = get_driver_instance(); 21 | con = driver->connect("tcp://127.0.0.1:3306", "black_fighter", "qweqwe"); 22 | con->setSchema("book_shop"); 23 | } 24 | virtual ~Connector() 25 | { 26 | delete con; 27 | } 28 | protected: 29 | sql::Driver* driver; 30 | sql::Connection* con; 31 | }; 32 | -------------------------------------------------------------------------------- /delete.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "connector.hpp" 19 | 20 | void error_log(const char* msg) 21 | { 22 | using namespace std; 23 | using namespace boost; 24 | static ofstream error; 25 | if(!error.is_open()) 26 | { 27 | error.open("/tmp/errlog", ios_base::out | ios_base::app); 28 | error.imbue(locale(error.getloc(), new posix_time::time_facet())); 29 | } 30 | error << '[' << posix_time::second_clock::local_time() << "] " << msg << endl; 31 | } 32 | 33 | class DeleteBook : public Connector 34 | { 35 | inline void sendError(const std::string& errorMsg) 36 | { 37 | out << "{ \"success\" : 0, \"message\" : \"" + errorMsg + "\" }" << std::endl; 38 | } 39 | inline void sendSuccess() 40 | { 41 | out << "{ \"success\" : 1 }" << std::endl; 42 | } 43 | bool response() 44 | { 45 | out << "Content-Type: application/json; charset=ISO-8859-1\r\n\r\n"; 46 | std::map parameters; 47 | for (Fastcgipp::Http::Environment::Posts::const_iterator it = environment().posts.begin(); it != environment().posts.end(); ++it) 48 | { 49 | parameters[it->first] = it->second.value; 50 | } 51 | if (parameters.find("id") == parameters.end()) 52 | { 53 | sendError("Missing id"); 54 | } 55 | else 56 | { 57 | 58 | sql::Statement* stmt = con->createStatement(); 59 | try 60 | { 61 | stmt->execute("DELETE FROM book WHERE id = " + parameters["id"]); 62 | sendSuccess(); 63 | } catch (sql::SQLException& e) 64 | { 65 | sendError(e.what()); 66 | } 67 | delete stmt; 68 | } 69 | return true; 70 | } 71 | }; 72 | 73 | int main() 74 | { 75 | try 76 | { 77 | Fastcgipp::Manager fcgi; 78 | fcgi.handler(); 79 | } 80 | catch (std::exception& e) 81 | { 82 | error_log(e.what()); 83 | } 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /get.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "connector.hpp" 19 | 20 | void error_log(const char* msg) 21 | { 22 | using namespace std; 23 | using namespace boost; 24 | static ofstream error; 25 | if(!error.is_open()) 26 | { 27 | error.open("/tmp/errlog", ios_base::out | ios_base::app); 28 | error.imbue(locale(error.getloc(), new posix_time::time_facet())); 29 | } 30 | error << '[' << posix_time::second_clock::local_time() << "] " << msg << endl; 31 | } 32 | 33 | class GetBook : public Connector 34 | { 35 | inline void sendError(const std::string& errorMsg) 36 | { 37 | out << "{ \"success\" : 0, \"message\" : \"" + errorMsg + "\" }" << std::endl; 38 | } 39 | bool response() 40 | { 41 | out << "Content-Type: application/json; charset=ISO-8859-1\r\n\r\n"; 42 | std::map parameters; 43 | for (Fastcgipp::Http::Environment::Gets::const_iterator it = environment().gets.begin(); it != environment().gets.end(); ++it) 44 | { 45 | parameters[it->first] = it->second; 46 | } 47 | if (parameters.find("id") == parameters.end()) 48 | { 49 | sendError("Missing id"); 50 | } 51 | else 52 | { 53 | sql::Statement* stmt = con->createStatement(); 54 | try 55 | { 56 | sql::ResultSet* res = stmt->executeQuery("SELECT name, publisher, UNIX_TIMESTAMP(date) as date, edition FROM book WHERE id = " + parameters["id"]); 57 | if (!res->next()) 58 | { 59 | sendError("Could not found book with id = " + parameters["id"]); 60 | } 61 | else 62 | { 63 | std::string result = "{ \"success\" : 1, "; 64 | result += "\"name\": \"" + res->getString("name") + "\","; 65 | result += "\"publisher\": \"" + res->getString("publisher") + "\","; 66 | result += "\"date\": " + res->getString("date") + ","; 67 | result += "\"edition\": " + res->getString("edition"); 68 | result += "}"; 69 | delete res; 70 | out << result << std::endl; 71 | } 72 | } catch (sql::SQLException& e) 73 | { 74 | sendError(e.what()); 75 | } 76 | delete stmt; 77 | } 78 | return true; 79 | } 80 | }; 81 | 82 | int main() 83 | { 84 | try 85 | { 86 | Fastcgipp::Manager fcgi; 87 | fcgi.handler(); 88 | } 89 | catch (std::exception& e) 90 | { 91 | error_log(e.what()); 92 | } 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /post.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "connector.hpp" 18 | 19 | void error_log(const char* msg) 20 | { 21 | using namespace std; 22 | using namespace boost; 23 | static ofstream error; 24 | if(!error.is_open()) 25 | { 26 | error.open("/tmp/errlog", ios_base::out | ios_base::app); 27 | error.imbue(locale(error.getloc(), new posix_time::time_facet())); 28 | } 29 | error << '[' << posix_time::second_clock::local_time() << "] " << msg << endl; 30 | } 31 | 32 | class PostBook : public Connector 33 | { 34 | inline void sendError(const std::string& errorMsg) 35 | { 36 | out << "{ \"success\" : 0, \"message\" : \"" + errorMsg + "\" }" << std::endl; 37 | } 38 | inline void sendSuccess() 39 | { 40 | out << "{ \"success\" : 1 }" << std::endl; 41 | } 42 | bool response() 43 | { 44 | out << "Content-Type: application/json; charset=ISO-8859-1\r\n\r\n"; 45 | std::map parameters; 46 | for (Fastcgipp::Http::Environment::Posts::const_iterator it = environment().posts.begin(); it != environment().posts.end(); ++it) 47 | { 48 | parameters[it->first] = it->second.value; 49 | } 50 | if (parameters.find("name") == parameters.end()) 51 | { 52 | sendError("Name is required"); 53 | } 54 | else if (parameters.find("publisher") == parameters.end()) 55 | { 56 | sendError("Publisher is required"); 57 | } 58 | else if (parameters.find("date") == parameters.end()) 59 | { 60 | sendError("Date is required"); 61 | } 62 | else if (parameters.find("edition") == parameters.end()) 63 | { 64 | sendError("Edition is required"); 65 | } 66 | else 67 | { 68 | // TODO 69 | } 70 | return true; 71 | } 72 | }; 73 | 74 | int main() 75 | { 76 | try 77 | { 78 | Fastcgipp::Manager fcgi; 79 | fcgi.handler(); 80 | } 81 | catch (std::exception& e) 82 | { 83 | error_log(e.what()); 84 | } 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /put.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "connector.hpp" 19 | 20 | void error_log(const char* msg) 21 | { 22 | using namespace std; 23 | using namespace boost; 24 | static ofstream error; 25 | if(!error.is_open()) 26 | { 27 | error.open("/tmp/errlog", ios_base::out | ios_base::app); 28 | error.imbue(locale(error.getloc(), new posix_time::time_facet())); 29 | } 30 | error << '[' << posix_time::second_clock::local_time() << "] " << msg << endl; 31 | } 32 | 33 | class PutBook : public Connector 34 | { 35 | inline void sendError(const std::string& errorMsg) 36 | { 37 | out << "{ \"success\" : 0, \"message\" : \"" + errorMsg + "\" }" << std::endl; 38 | } 39 | inline void sendSuccess() 40 | { 41 | out << "{ \"success\" : 1 }" << std::endl; 42 | } 43 | bool response() 44 | { 45 | out << "Content-Type: application/json; charset=ISO-8859-1\r\n\r\n"; 46 | std::map parameters; 47 | for (Fastcgipp::Http::Environment::Posts::const_iterator it = environment().posts.begin(); it != environment().posts.end(); ++it) 48 | { 49 | parameters[it->first] = it->second.value; 50 | } 51 | if (parameters.find("id") == parameters.end()) 52 | { 53 | sendError("Missing id"); 54 | } 55 | else 56 | { 57 | std::map columns; 58 | if (parameters.find("name") != parameters.end()) 59 | { 60 | columns["name"] = "\"" + parameters["name"] + "\""; 61 | } 62 | if (parameters.find("publisher") != parameters.end()) 63 | { 64 | columns["publisher"] = "\"" + parameters["publisher"] + "\""; 65 | } 66 | if (parameters.find("date") != parameters.end()) 67 | { 68 | columns["date"] = "FROM_UNIXTIME('" + parameters["date"] + "')"; 69 | } 70 | if (parameters.find("edition") != parameters.end()) 71 | { 72 | columns["edition"] = parameters["edition"]; 73 | } 74 | if (columns.empty()) 75 | { 76 | sendError("There is no column to be updated"); 77 | } 78 | else 79 | { 80 | std::string query = "UPDATE book SET "; 81 | for (std::map::iterator it = columns.begin(); it != columns.end(); ++it) 82 | { 83 | if (it != columns.begin()) query += ", "; 84 | query += it->first + "=" + it->second; 85 | } 86 | query += " WHERE id=" + parameters["id"]; 87 | sql::Statement* stmt = con->createStatement(); 88 | try 89 | { 90 | stmt->execute(query); 91 | sendSuccess(); 92 | } catch (sql::SQLException& e) 93 | { 94 | sendError(e.what()); 95 | } 96 | delete stmt; 97 | } 98 | } 99 | return true; 100 | } 101 | }; 102 | 103 | int main() 104 | { 105 | try 106 | { 107 | Fastcgipp::Manager fcgi; 108 | fcgi.handler(); 109 | } 110 | catch (std::exception& e) 111 | { 112 | error_log(e.what()); 113 | } 114 | return 0; 115 | } 116 | --------------------------------------------------------------------------------