[Webserver]httplib webserver+sqlite(MacOS)

๐Ÿ‘‰๐Ÿป C++์˜ httplib๋ฅผ ์‚ฌ์šฉํ•œ ๋งค์šฐ ๊ฐ„๋‹จํ•œ ์›น์„œ๋ฒ„ ์ž…๋‹ˆ๋‹ค.
This is a very simple web server using C++’s httplib.

๐Ÿ‘‰๐Ÿป sqllite๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
Added SQLite database.

๐Ÿ‘‰๐Ÿป ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ ์‚ญ์ œ ๋ฐ ์ „์ฒด๋ฐ์ดํ„ฐ ๋ณด๊ธฐ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
You can add, delete, and view all data.

๐Ÿ‘‰๐Ÿป์ฝ”๋“œ / code

โœ”๏ธ httplib.h

— ์•„๋ž˜์˜ ๋ช…๋ น์–ด๋กœ ๋‹ค์šด๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
Download using the command below.

โœ”๏ธ server.cpp

#include "httplib.h"
#include <sqlite3.h>
#include <iostream>

int main( ) {
    httplib::Server svr;
    sqlite3* db;

    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŒŒ์ผ ์—ด๊ธฐ (์—†์œผ๋ฉด ์ƒ์„ฑ๋จ )
    // Open database file (creates if it does not exist)
    if (sqlite3_open("test.db", &db)) {
        std::cerr << "DB open failed!" << std::endl;
        return 1;
    }
    // ๋ฃจํŠธ ๋ผ์šฐํ„ฐ(html์ ์šฉ)
    // Root Router (HTML applied)
    svr.Get("/",[](const httplib::Request &req, httplib::Response &res){
     res.set_content("<h1>Hello! silver hand!!!๐Ÿ‘‹</h1>","text/html; charset=utf-8");
    });

    // ํ…Œ์ด๋ธ” ์ƒ์„ฑ ๋ฐ ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ์ž…๋ ฅ
    // Create table and input initial data
    svr.Get("/init", [&](const httplib::Request&, httplib::Response& res ) {
        char* errMsg = nullptr;

        // ํ…Œ์ด๋ธ” ์ƒ์„ฑ SQL
        // table creation SQL
        const char* create_sql = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);";
        if (sqlite3_exec(db, create_sql, nullptr, nullptr, &errMsg) != SQLITE_OK) {
            res.set_content("{\"error\": \"Table creation failed: " + std::string(errMsg) + "\"}", "application/json");
            sqlite3_free(errMsg);
            return;
        }

        // ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์‚ฝ์ž… (Silver Hand ์ถ”๊ฐ€)
        // Insert test data (Add Silver Hand)
        const char* insert_sql = "INSERT INTO users (name) VALUES ('Silver Hand ๐Ÿ‘‹');";
        if (sqlite3_exec(db, insert_sql, nullptr, nullptr, &errMsg) != SQLITE_OK) {
            res.set_content("{\"error\": \"Insert failed: " + std::string(errMsg) + "\"}", "application/json");
            sqlite3_free(errMsg);
            return;
        }

        res.set_content("{\"message\": \"Table created and data initialized!\"}", "application/json; charset=utf-8");
    });


    // API ์š”์ฒญ ์‹œ DB ์กฐํšŒํ•˜๊ธฐ
    // Querying DB when making API requests
    svr.Get("/user", [&](const httplib::Request&, httplib::Response& res ) {
        // ๊ฐ€์žฅ ์ตœ๊ทผ์— ์ถ”๊ฐ€๋œ ์‚ฌ์šฉ์ž 1๋ช…์˜ ์ด๋ฆ„์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
        const char* sql = "SELECT name FROM users ORDER BY id DESC LIMIT 1;";
        sqlite3_stmt* stmt;

        if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) == SQLITE_OK) {
            if (sqlite3_step(stmt) == SQLITE_ROW) {
                const char* name = (const char*)sqlite3_column_text(stmt, 0);
                res.set_content("{\"username\": \"" + std::string(name ? name : "") + "\"}", "application/json; charset=utf-8");
            } else {
                res.status = 404;
                res.set_content("{\"error\": \"No users found. Please run /init first.\"}", "application/json; charset=utf-8");
            }
            sqlite3_finalize(stmt);
        } else {
            res.set_content("{\"error\": \"Query failed\"}", "application/json");
        }
    });


    // ์ „์ฒด ์‚ฌ์šฉ์ž ๋ชฉ๋ก ์กฐํšŒ
    // View all user list
    svr.Get("/users", [&](const httplib::Request&, httplib::Response& res ) {
        const char* sql = "SELECT id, name FROM users;";
        sqlite3_stmt* stmt;
        std::string json = "[";

        if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) == SQLITE_OK) {
            bool first = true;
            while (sqlite3_step(stmt) == SQLITE_ROW) {
                if (!first) json += ",";
                int id = sqlite3_column_int(stmt, 0);
                const char* name = (const char*)sqlite3_column_text(stmt, 1);
                json += "{\"id\":" + std::to_string(id) + ", \"name\":\"" + (name ? name : "") + "\"}";
                first = false;
            }
            sqlite3_finalize(stmt);
        }
        json += "]";
        res.set_content(json, "application/json; charset=utf-8");
    });

    // ์ „์ฒด ๋ฐ์ดํ„ฐ ์‚ญ์ œ
    // Delete all data
    svr.Get("/clear", [&](const httplib::Request&, httplib::Response& res ) {
        char* errMsg = nullptr;
        //const char* delete_sql = "DELETE FROM users;";
        //๋ฐ์ดํ„ฐ ์‚ญ์ œ + ID ์นด์šดํ„ฐ ์ดˆ๊ธฐํ™” / Delete all data and reset ID counter
        const char* delete_sql = "DELETE FROM users; DELETE FROM sqlite_sequence WHERE name='users';";


        if (sqlite3_exec(db, delete_sql, nullptr, nullptr, &errMsg) != SQLITE_OK) {
            res.set_content("{\"error\": \"Delete failed: " + std::string(errMsg) + "\"}", "application/json");
            sqlite3_free(errMsg);
            return;
        }

        res.set_content("{\"message\": \"All data cleared!\"}", "application/json; charset=utf-8");
    });


    std::cout << "server listen 0.0.0.0:8080" << std::endl;
    svr.listen("0.0.0.0", 8080);

    sqlite3_close(db);
    return 0;
}

๐Ÿ‘‰๐Ÿป ์ปดํŒŒ์ผ / Compiling

g++ -o server server.cpp -std=c++11 -pthread -lsqlite3

โœ”๏ธ ์„œ๋ฒ„์‹คํ–‰ / Run server

./server

โœ”๏ธ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ผ์šฐํŠธ ์‹คํ–‰ / Run route in browser

— ๋ฃจํŠธ ๋ผ์šฐํŠธ / root route

/

— ๋ฐ์ดํ„ฐ์ž…๋ ฅ ๋ผ์šฐํŠธ / Data input route

/init

— ๋ฐ์ดํ„ฐ ์ž…๋ ฅํ™•์ธ ๋ผ์šฐํŠธ / Data input verification route

/user

— ์ „์ฒด ๋ฐ์ดํ„ฐ ํ™•์ธ / Check full data

/users

Leave a Reply