Pistache:用 C++ 寫 RESTful API Server
Pistache 是一個用 C++ 11 標準寫的 RESTful API library。除了 Server 以外,它也帶了不錯的 Client 端方法,不過今天主要會放在撰寫 Server 的部分。
Pistache 是一個用 C++ 11 標準寫的 RESTful API library。除了 Server 以外,它也帶了不錯的 Client 端方法,不過今天主要會放在撰寫 Server 的部分。
為什麼要用 C++ 寫 API Server
最近在寫一個東西,原本只是要寫一個 Web 介面,然後讀檔寫檔而已。換句話說大概會有這樣的架構:
GET
/
:回傳index.html
。POST
/save
:把表單寫入到某個檔案。GET
/load
:從某個檔案讀出 json 格式。
應該是個用 Koa 或 Flask 可以簡單寫完的東西。但是這次寫東西要在 Raspberry Pi Zero 上跑,而且它同時還有跑其他東西,CPU 都已經快吃完了,所以才會用 C++ 試著寫 RESTful Server。
環境安裝
首先你可能需要把 gcc、g++、make、cmake、git 安裝起來(以 Ubuntu 為例):
sudo apt-get install gcc g++ make cmake git
接著把 Pistache clone 下來並更新:
git clone https://github.com/oktal/pistache.git
cd pistache
git submodule update --init
接著安裝編譯並安裝 Pistache:
cd pistache
mkdir build
cd build
cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ..
make
sudo make install
這樣就能把 Pistache 安裝起來,稍後寫 C++ 的時候就可以直接 #include <pistache/xxx.h>
了。
Hello, world! 範例
這是官方 GitHub 給的 Hello, world! 範例,可以先試著打開一個檔案叫 server.cc
,並貼上以下內容:
#include <pistache/endpoint.h>
using namespace Pistache;
struct HelloHandler : public Http::Handler {
HTTP_PROTOTYPE(HelloHandler)
void onRequest(const Http::Request&, Http::ResponseWriter writer) override{
writer.send(Http::Code::Ok, "Hello, World!");
}
};
int main() {
Http::listenAndServe<HelloHandler>("*:9080");
}
接著使用 g++
編譯:
g++ --std=c++11 server.cc -lpistache -o server
編譯好之後應該會看到一個 server
執行檔,直接執行 ./server
就可以在瀏覽器打開 https://localhost:9080 ,應該會看到一個 Hello, World! 字樣。
RESTful API 範例
官方的 GitHub 上也給了一個不錯好上手的 RESTful API 範例,程式碼有點長,這邊先貼一份連結 rest_server.cc。
範例中在 setupRoutes
方法就定義好 router 的路徑了:
Routes::Post(router, "/record/:name/:value?", Routes::bind(&StatsEndpoint::doRecordMetric, this));
Routes::Get(router, "/value/:name", Routes::bind(&StatsEndpoint::doGetMetric, this));
Routes::Get(router, "/ready", Routes::bind(&Generic::handleReady));
Routes::Get(router, "/auth", Routes::bind(&StatsEndpoint::doAuth, this));
這邊看起來是 POST
/record/:name/:value
時要執行 doRecordMetric
方法,看起來跟 Koa 的用法差不多。可以參考範例檔案裡面的設定來新增方法,例如:
class StatsEndpoint {
public:
/* ... */
private:
void setupRoutes () {
Routes::Get(router, "/index.html", Routes::bind(&StatsEndpoint::doServe, this);
}
void doServe(const Rest::Request& req, Http::ResponseWriter res) {
Http::serveFile(res, "public/index.html");
}
}
這邊補充一下常用到的幾個方法:
如果要直接 serve 一個靜態檔案可以在 router 裡這樣寫:
Http::serveFile(res, "/path/to/file.txt");
如果要回傳內容可以這樣寫:
res.send(Http::Code::Ok, "Hello, World!");
如果要回傳 JSON 內容,可以先帶個表頭並回傳內容:
std::string json_output = "{"test": true}";
res.headers().add<Http::Header::ContentType>(MIME(Text, Json));
res.send(Http::Code::Ok, json_output);
這樣就可以用 C++ 寫出一個簡單的 server 了。