C++ で REST API を触るときに簡単にできるものがないかな、と探してたどり着いたのが CPPRESTSDK でした。これは Microsoft が GitHub で公開しています。
この記事を書いている時点ではバージョンは 2.10.17 で、2021年に入ってとりあえず更新もあり、まだ活きてはいるプロダクトと考えています。
対応プラットフォーム
割と動作プラットフォームが多めです。
- Windows
- Debian/Ubuntu
- Fedora
- OSX
- Android (?)
これらの対応範囲であれば、標準的なパッケージ管理システムでのインストールも出来るようです(GitHubのページを参照)。
使い方
自分のメモ用ですが、簡単に使い方を記録しておきます。
Get リクエスト
template<class T>
pplx::task<T> Get(const std::string& url) {
return pplx::create_task([=]{
http_client_config config;
config.set_timeout(seconds(5));
http_client client(url, config);
return client.request(methods::GET);
}).then( [](http_response response)
{
if( response.status_code() != status_codes::OK) {
throw std::runtime_error("network error");
}
return response.extract_json();
});
}
このようなコードで Get リクエストを送信して、JSON の結果を取得します。task<T> で受け取るようにしているので、呼び出し元ではさらに then によって処理をつなげることを想定しています。
Post リクエスト
pplx::task<void> Post(const std::string& url, const web::json::value& data) {
return pplx::create_task([=]{
http_client_config config;
config.set_timeout(seconds(5));
http_client client(url, config);
return client.request(methods::POST, "", data.serialize(), "application/json");
}).then([](http_response response) {
auto code = response.status_code();
if( response.status_code() == status_codes::OK) {
return;
}
throw std::runtime_error("network error");
});
}
このようなコードで Post リクエストを送信します。JSON データを送るために引数にしています。この処理結果は task<void> なので、処理をつなげることは想定していません。
使ってみて苦労した点
投げっぱなし処理にするには
リクエストを送信して、結果が返ってくるまで待つ、という流れで使うのであれば苦労しないと思います。各種 task 戻り値のオブジェクトで wait() と呼び出して終わりです。今回使った場面ではネットワーク処理によって待機することは避けたいということもあり、これをどうにかする必要がありました。
ちなみに投げっぱなしで良いので、放置というスタイルを採ると処理完了後にクラッシュするようです。そのため wait() を呼ばずに済ます、というのはなさそうです。
自分が採った方法は、処理を行うスレッドを作成しそこで wait させる、でした。C++で標準的に使えるようになった std::thread を用いて以下のようなコードを作成しています。
std::thread netThr( [=](){
try {
auto task = Get<json::value>( ... );
task.then( [](json::value) { /* 処理*/ } );
task.wait();
} catch(http_exception e) {
/* エラー処理 */
}
});
netThr.detach();
投げっぱなしで良かったため、detach を呼び出しています。これをしないと join を実行する必要があるためです。
ひとまずこのコードは、手元の Raspberry Pi 4 (Raspberry Pi OS) でも動作しています。
extract_json の罠
リクエストを送出してレスポンスが JSON で返ってくるからと、 extract_json() を呼び出したら例外が出るという罠がありました。これは仕様のようで、そもそもサーバーからのレスポンスが間違っていたというのがオチだったのですが、なかなか気付くことが出来ませんでした。
このメソッドを呼び出すときには、 Content-Type で application/json というように json タイプであることが明記されているか確認したほうが良いと思いました。出遭ったケースでは text/plain で json を返していました。