JSON : Placeholder (https://jsonplaceholder.typicode.com/) 是一個用於測試的 REST API 網站。
如下使用 Task API/Rx.NET + Json.NET 調用該網站的 REST API,獲取字符串以及 JSON 數據。ios
全部 GET API 都返回JSON數據,格式(JSON-Schema)以下:git
{ "type":"object", "properties": { "userId": {"type" : "integer"}, "id": {"type" : "integer"}, "title": {"type" : "string"}, "body": {"type" : "string"} } }
$ brew install cpprestsdk $ brew install boost $ brew install libressl
$ brew tap nlohmann/json $ brew install nlohmann_json
$ git clone --recursive https://github.com/ReactiveX/RxCpp.git
打開 Xcode,File / New / Project...
在嚮導的第1頁選 macOS / Command Line Tool
在嚮導的第2頁語言選 C++,Product Name 填上任意名稱
在嚮導的第3頁選擇任意文件夾,點擊 Create 建立工程。github
將 System Header Search Paths 設置爲
/usr/local/Cellar/cpprestsdk/2.10.2/include
/usr/local/Cellar/boost/1.67.0_1/include
/usr/local/Cellar/libressl/2.7.4/include
/usr/local/Cellar/nlohmann_json/3.1.2/include
RxCpp安裝文件夾/Rx/v2/srcweb
將 Library Search Paths 設置爲
/usr/local/Cellar/cpprestsdk/2.10.2/lib
/usr/local/Cellar/boost/1.67.0_1/lib
/usr/local/Cellar/libressl/2.7.4/libjson
將 Other Linker Flags 設置爲
-lcpprest -lboost_system -lboost_thread-mt -lboost_chrono-mt -lssl -lcryptoapi
cpprestsdk: Undefined symbols for architecture x86_64app
在工程中添加 post.hpp,內容以下ide
#ifndef Post_hpp #define Post_hpp #include <iostream> #include <nlohmann/json.hpp> using nlohmann::json; struct Post { int userId; int id; std::string title; std::string body; }; void to_json(json& j, const Post& p); void from_json(const json& j, Post& p); std::ostream& operator<<(std::ostream& out, const Post& p); #endif /* Post_hpp */
在工程中添加 post.cpp,內容以下post
#include "Post.hpp" #include <boost/algorithm/string/replace.hpp> using namespace std; void to_json(json& j, const Post& p) { j = json{{"userId", p.userId}, {"id", p.id}, {"title", p.title}, {"body", p.body}}; } void from_json(const json& j, Post& p) { p.userId = j.at("userId").get<int>(); p.id = j.at("id").get<int>(); p.title = j.at("title").get<string>(); p.body = boost::algorithm::replace_all_copy(j.at("body").get<string>(), "\n", "\\n"); } std::ostream& operator<<(std::ostream& out, const Post& p) { cout << "Post {userId = " << p.userId << ", id = " << p.id << ", title = \"" << p.title << "\", body = \"" << p.body << "\"}"; return out; }
在工程中添加 RestApi.hpp,內容以下測試
#ifndef RestApi_hpp #define RestApi_hpp #include <cpprest/http_client.h> #include <cpprest/filestream.h> #include <cpprest/json.h> using namespace utility; // Common utilities like string conversions //using namespace web; // Common features like URIs. using namespace web::http; // Common HTTP functionality using namespace web::http::client; // HTTP client features using namespace concurrency::streams; // Asynchronous streams #include "rxcpp/rx.hpp" namespace Rx { using namespace rxcpp; using namespace rxcpp::sources; using namespace rxcpp::operators; using namespace rxcpp::util; } using namespace Rx; #include <nlohmann/json.hpp> using nlohmann::json; template<class T> struct RestApi { http_client client; RestApi(const uri &base_uri) : client(base_uri) {} observable<string_t> getString(const string_t &path_query_fragment) { return observable<>::create<string_t>( [&](subscriber<string_t> s){ client .request(methods::GET, path_query_fragment) .then([](http_response response) -> pplx::task<string_t> { return response.extract_string(); }) .then([&](pplx::task<string_t> previousTask) { try { string_t const & v = previousTask.get(); s.on_next(v); s.on_completed(); } catch (http_exception const & e) { s.on_error(std::current_exception()); } }) .wait(); }); } observable<T> getObject(const string_t &path_query_fragment) { return observable<>::create<T>( [&](subscriber<T> s){ client .request(methods::GET, path_query_fragment) .then([](http_response response) -> pplx::task<string_t> { return response.extract_string(); }) .then([&](pplx::task<string_t> previousTask) { try { string_t const & v = previousTask.get(); json j = json::parse(v); T t = j; s.on_next(t); s.on_completed(); } catch (http_exception const & e) { s.on_error(std::current_exception()); } }) .wait(); }); } observable<T> getArray(const string_t &path_query_fragment) { return observable<>::create<T>( [&](subscriber<T> s){ client .request(methods::GET, path_query_fragment) .then([](http_response response) -> pplx::task<string_t> { return response.extract_string(); }) .then([&](pplx::task<string_t> previousTask) { try { string_t const & v = previousTask.get(); json j = json::parse(v); std::vector<T> vec = j; for(const auto& t : vec) s.on_next(t); s.on_completed(); } catch (http_exception const & e) { s.on_error(std::current_exception()); } }) .wait(); }); } observable<string_t> createObject(const string_t& url, const T& obj) { return observable<>::create<string_t>( [&](subscriber<string_t> s){ json j = obj; client .request(methods::POST, url, j.dump(), U("application/json")) .then([](http_response response) -> pplx::task<string_t> { return response.extract_string(); }) .then([&](pplx::task<string_t> previousTask) { try { string_t const & v = previousTask.get(); s.on_next(v); s.on_completed(); } catch (http_exception const & e) { s.on_error(std::current_exception()); } }) .wait(); }); } observable<string_t> updateObject(const string_t& url, const T& obj) { return observable<>::create<string_t>( [&](subscriber<string_t> s){ json j = obj; client .request(methods::PUT, url, j.dump(), U("application/json")) .then([](http_response response) -> pplx::task<string_t> { return response.extract_string(); }) .then([&](pplx::task<string_t> previousTask) { try { string_t const & v = previousTask.get(); s.on_next(v); s.on_completed(); } catch (http_exception const & e) { s.on_error(std::current_exception()); } }) .wait(); }); } observable<string_t> deleteObject(const string_t& url) { return observable<>::create<string_t>( [&](subscriber<string_t> s){ client .request(methods::DEL, url) .then([](http_response response) -> pplx::task<string_t> { return response.extract_string(); }) .then([&](pplx::task<string_t> previousTask) { try { string_t const & v = previousTask.get(); s.on_next(v); s.on_completed(); } catch (http_exception const & e) { s.on_error(std::current_exception()); } }) .wait(); }); } }; #endif /* RestApi_hpp */
將 main.cpp 的內容改成
#include "Post.hpp" #include "RestApi.hpp" using namespace std; int main(int argc, char* argv[]) { RestApi<Post> api(U("https://jsonplaceholder.typicode.com/")); api.getString(U("posts/1")).subscribe([](const string_t& v){cout << v << endl;}); api.getObject(U("posts/1")).subscribe([](const Post& v){cout << v << endl;}); api.getArray(U("posts")).take(2).subscribe([](const Post& v){cout << v << endl;}); Post o; o.id = 0; o.userId = 101; o.title = U("test title"); o.body = U("test body"); api.createObject(U("posts"), o).subscribe([](string_t v){cout << v << endl;}); api.updateObject(U("posts/1"), o).subscribe([](string_t v){cout << v << endl;}); api.deleteObject(U("posts/1")).subscribe([](string_t v){cout << v << endl;}); return 0; }
{ "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" } Post {userId = 1, id = 1, title = "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", body = "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"} Post {userId = 1, id = 1, title = "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", body = "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"} Post {userId = 1, id = 2, title = "qui est esse", body = "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"} { "body": "test body", "id": 101, "title": "test title", "userId": 101 } { "body": "test body", "id": 1, "title": "test title", "userId": 101 } {}