簡(jiǎn)介
c++REST SDK琼懊,又叫卡薩布蘭卡是一個(gè)微軟發(fā)布的C++基于云的客戶機(jī)-服務(wù)器通信庫(kù)。該庫(kù)基于現(xiàn)代化的C++異步API爬早,即Promise模型或叫鏈?zhǔn)疆惒侥P驮O(shè)計(jì)[1]哼丈,c++開(kāi)發(fā)人員可以方便地連接并與服務(wù)交互。
SDK內(nèi)容
- 特性——HTTP客戶機(jī)/服務(wù)器筛严,JSON醉旦,URI,異步流桨啃,WebSockets客戶機(jī),oAuth
- PPL任務(wù)[2] ——一個(gè)強(qiáng)大的基于c++11特性編寫(xiě)的異步操作模型
- 支持平臺(tái)——Windows桌面车胡,Windows Store,Windows Phone照瘾,Ubuntu匈棘,OS X,iOS和Android
- Windows平臺(tái)編譯支持——VS 2012析命,2013年和2015
- 非Windows平臺(tái)編譯支持——cmake
- 包管理器支持——NuGet主卫,僅在VS編譯器支持Windows和Android平臺(tái)[3]
Http Client示例
#include <cpprest/http_client.h>
#include <cpprest/filestream.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
int main(int argc, char* argv[])
{
auto fileStream = std::make_shared<ostream>();
// Open stream to output file.
pplx::task<void> requestTask = fstream::open_ostream(U("results.html")).then([=](ostream outFile)
{
*fileStream = outFile;
// Create http_client to send the request.
http_client client(U("http://www.bing.com/"));
// Build request URI and start the request.
uri_builder builder(U("/search"));
builder.append_query(U("q"), U("cpprestsdk github"));
return client.request(methods::GET, builder.to_string());
})
// Handle response headers arriving.
.then([=](http_response response)
{
printf("Received response status code:%u\n", response.status_code());
// Write response body into the file.
return response.body().read_to_end(fileStream->streambuf());
})
// Close the file stream.
.then([=](size_t)
{
return fileStream->close();
});
// Wait for all the outstanding I/O to complete and handle any exceptions
try
{
requestTask.wait();
}
catch (const std::exception &e)
{
printf("Error exception:%s\n", e.what());
}
return 0;
}
JSON
構(gòu)造JSON
JSON是JavaScript Object Notation,也就是JS對(duì)象表示法的簡(jiǎn)稱鹃愤。由于它的簡(jiǎn)單簇搅,緊湊,靈活软吐,占用空間小瘩将,與JS無(wú)縫銜接等優(yōu)點(diǎn),近年來(lái)已經(jīng)替代xml成為web通信的主要信息載體关噪。在cpprest中鸟蟹,JSON值是由web::json::value類來(lái)表示的,不管它是一個(gè)數(shù)值使兔,一個(gè)字符串,或者一個(gè)對(duì)象藤韵,它都可以是一個(gè)JSON值虐沥。正是由于JSON可以表示任何靜態(tài)數(shù)據(jù)類型,所以有不少語(yǔ)言有不少庫(kù)支持JSON對(duì)象和編程語(yǔ)言對(duì)象的直接映射泽艘,但這些庫(kù)在提高編碼效率的同時(shí)欲险,也都有或多或少的問(wèn)題,有的有性能問(wèn)題匹涮,有的對(duì)代碼有侵入性天试,如必須繼承某一基類,需要謹(jǐn)慎選用然低。
構(gòu)建一個(gè)JSON值喜每,最簡(jiǎn)單的方式务唐,我們可以通過(guò)使用普通的c++值來(lái)構(gòu)建。cpprest提供了一個(gè)重載的工廠函數(shù)带兜,該函數(shù)可以構(gòu)建6種類型的JSON值枫笛,示例如下:
using namespace web;
...
json::value v0 = json::value::null();
json::value v1 = json::value::number(17);
json::value v2 = json::value::number(3.1415);
json::value v3 = json::value::boolean(true);
json::value v4 = json::value::string(U("Hello Again!"));
json::value v5 = json::value::object();
json::value v6 = json::value::array();
解析和序列化
對(duì)于JSON來(lái)說(shuō),幾乎所有實(shí)際中的使用刚照,都是將類型轉(zhuǎn)化成JSON或從JSON轉(zhuǎn)化成相應(yīng)的類型刑巧,所以一個(gè)常見(jiàn)的構(gòu)建JSON的方式,就是通過(guò)解析无畔。
我們可以通過(guò)解析的方法從一個(gè)流或字符串中生成一個(gè)JSON啊楚,如:
using namespace web;
...
utility::stringstream_t ss1;
ss1 << U("17");
json::value v1 = json::value::parse(ss1);
相反的方向也同樣簡(jiǎn)單:
using namespace web;
...
utility::stringstream_t stream;
json::value v1 = json::value::string(U("Hi"));
v1.serialize(stream);
訪問(wèn)數(shù)據(jù)
除了添加元素,添加字段和寫(xiě)JSON值到一個(gè)流中浑彰,對(duì)一個(gè)JSON來(lái)說(shuō)特幔,沒(méi)太多操作可做:因?yàn)镴SON并不是用于作為一個(gè)通用的動(dòng)態(tài)數(shù)值系統(tǒng)來(lái)設(shè)計(jì)的,他的主要作用就是對(duì)JSON對(duì)象做讀寫(xiě)操作闸昨。對(duì)于一個(gè)的值處理蚯斯,還是應(yīng)該仰仗于C++系統(tǒng),所以我們就需要一種從JSON對(duì)象得到C++值的方法饵较。cpprest強(qiáng)迫我們從JSON中取值時(shí)拍嵌,通過(guò)使用"as_xxx()"這樣的函數(shù)來(lái)明確指定C++類型,而不是提供隱式轉(zhuǎn)換操作循诉,如:
int i = v1.as_integer();
double d = v2.as_double();
bool b = v3.as_bool();
utility::string_t s = v4.as_string();
如果JSON內(nèi)部數(shù)據(jù)跟我們要求的類型不一致時(shí)横辆,轉(zhuǎn)換將拋出一個(gè)類型為json::json_exception的異常。如茄猫,當(dāng)要求轉(zhuǎn)換一個(gè)字符串到double或布爾時(shí)狈蚤,就發(fā)生失敗。訪問(wèn)JSON數(shù)組的單個(gè)或?qū)ο箢愋偷某蓡T變量有幾種方式划纽。其中一種方法就是使用[]函數(shù)脆侮,下標(biāo)操作符是非“常量”的操作,并且可以修改JSON值勇劣,在必要時(shí)靖避,會(huì)添加一個(gè)null值。
json::value obj = json::value::parse(U("{ \"a\" : 10 }"));
obj[U("a")] = json::value(12);
obj[U("b")] = json::value(13);
auto nullValue = obj[U("c")];
在上面的代碼中比默,字符串"a"的值幻捏,將從10變?yōu)?2,將在obj中添加一個(gè)"b"命咐,其值為13篡九,由于obj中沒(méi)有"c",直接讀取"c"時(shí)醋奠,將會(huì)返回一個(gè)null值榛臼。
通過(guò)不插入任何職方式訪問(wèn)JSON數(shù)組和對(duì)象時(shí)伊佃,可以使用json::value::at方法。如果值存在讽坏,則該方法就返回一個(gè)JSON值得引用锭魔,如果不存在,則拋出一個(gè)json::json_exeption異常路呜。
json::value obj = json::value::parse(U("{ \"a\" : 10 }"));
auto aValue = obj.at(U("a"));
auto bValue = obj.at(U("b"));
在上面的代碼中迷捧,調(diào)用"at"方法,訪問(wèn)"a"胀葱,將返回一個(gè)JSON數(shù)值10漠秋,而訪問(wèn)"b"將拋出異常。我們可以通過(guò)json::value::size()和json::value::has_field來(lái)檢測(cè)大小和一個(gè)JSON字段是否存在抵屿。
相比于其他類型庆锦,數(shù)值型操作,會(huì)多一個(gè)動(dòng)作轧葛,指定具體是哪種類型的數(shù)值搂抒,如:
json::value num = json::value(88);
int64_t num64 = num.as_number().to_int64();
WebSocket客戶端
關(guān)于websocekts所有相關(guān)的東西都在頭文件:ws_client.h
,命名空間:web::web sockets::client
尿扯。
#include <cpprest/ws_client.h>
using namespace web;
using namespace web::websockets::client;
類 websocket_client
被用來(lái)創(chuàng)建 和維持一個(gè)到WebSocket端點(diǎn)的連接求晶。一旦你有你的客戶端,你就必須使用函數(shù)'connect()'連接到一個(gè)遠(yuǎn)端,并且傳入一個(gè)該客戶端要連接的URI衷笋,該函數(shù)會(huì)返回一個(gè)可以等待的pplx::task
芳杏。
websocket_client client;
client.connect(U("ws://localhost:1234")).then([](){ /* We've finished connecting. */ });
一旦客戶端連接上,你就可以開(kāi)始發(fā)送和接受數(shù)據(jù)辟宗。就跟C++ Rest SDK的rest部分一樣爵赵,這是一異步的方式完成的。
websocket_outgoing_message msg;
msg.set_utf8_message("I am a UTF-8 string! (Or close enough...)");
client.send(msg).then([](){ /* Successfully sent the message. */ });
client.receive().then([](websocket_incoming_message msg) {
return msg.extract_string();
}).then([](std::string body) {
std::cout << body << std::endl;
});
(注意:每來(lái)一條消息泊脐,只有一個(gè)'receive()'函數(shù)會(huì)被調(diào)用)
我們支持發(fā)送和接收字符串和二進(jìn)制消息空幻。
websocket_outgoing_message msg;
concurrency::streams::producer_consumer_buffer<uint8_t> buf;
std::vector<uint8_t> body(6);
memcpy(&body[0], "a\0b\0c\0", 6);
auto send_task = buf.putn(&body[0], body.size()).then([&](size_t length) {
msg.set_binary_message(buf.create_istream(), length);
return client.send(msg);
}).then([](pplx::task<void> t)
{
try
{
t.get();
}
catch(const websocket_exception& ex)
{
std::cout << ex.what();
}
});
send_task.wait();
一旦結(jié)束,我們就應(yīng)該關(guān)閉它晨抡。
client.close().then([](){ /* Successfully closed the connection. */ });
有時(shí)甚至是大部分時(shí)候你從服務(wù)端接收許多消息氛悬,需要不斷地調(diào)用websocket_client::receive()
并處理每個(gè)任務(wù),肯定是很繁瑣而且易錯(cuò)的耘柱,這時(shí)我們有另一個(gè)類websocket_callback_client
,它允許設(shè)置一個(gè)回調(diào)函數(shù)來(lái)接收從服務(wù)器發(fā)來(lái)的消息棍现。注冊(cè)回調(diào)函數(shù)的實(shí)例如下:
websocket_callback_client client;
client.connect(U("ws://localhost:1234")).then([](){ /* We've finished connecting. */ });
// set receive handler
client.set_message_handler([](websocket_incoming_message msg)
{
// handle message from server...
});
-
微軟把它稱作基于任務(wù)的異步編程 ?
-
PPL调煎,全稱為Parallel Patterns Library,并行模式庫(kù)己肮,在cpprest中改名為pptx士袄,為了與ppl共存(ppl默認(rèn)存在于VS2012以上版本中) ?
-
部分語(yǔ)言包管理器:1 蘋(píng)果的XCode支持CocoaPods管理器悲关,同時(shí)支持Objective C和Swift開(kāi)發(fā)OS X和iOS應(yīng)用。 2 Java分別有ant(在主流項(xiàng)目開(kāi)發(fā)中娄柳,基本棄用)寓辱,maven(用xml管理第三方庫(kù))和gradle(用基于Groovy的動(dòng)態(tài)DSL管理第三方庫(kù)) ?