http_client代码示例
#include
#ifdef _WIN32
#include
#include
#endif
#include
using namespace utility;
using namespace web::http;
using namespace web::http::client;
using namespace std::cerr;
using namespace std::endl;
#ifdef _WIN32 //根据平台来定义tcout,确保多语言的文字能够正确输出
#define tcout std::wcout;
#else
#define tcout std::cout;
#endif
//从http_response中取出头部的字符串表示
auto get_headers(http_response resp) {
auto headers = resp.to_string();
auto end = headers.find("\r\n\r\n");
if (end != string_t::npos) {
headers.resize(end + 4);
}
return headers;
}
auto get_request(string_t uri) {
http_client client{
uri };
//用GET的方式发起一个客户端请求
//并使用then方法串联了两个下一步动作
//http_client::request的返回值是pplx::task.then是pplx::task类模板的成员函数,参数是能接受其类型参数对象的函数对象,
//除了最后一个then,其他每个then里都应该返回一个pplx::task,而task的内部类型,就是下一个then块里函数对象接受的参数类型。
auto request = client.request(methods::GET).then([](http_response resp) {
if (resp.status_code() != status_codes::OK) {
//不OK,显示当前响应信息
auto headers = get_headers(resp);
tcout << headers;
}
//进一步取出完整响应
return resp.extract_string();
}).then([](string_t str) {
//输出到终端
tcout << str;
});
return request;
}
#ifdef _WIN32 //根据平台定义合适的程序入口
int wmian(int argc, wchar_t* argv[])
#else
int main(int argc, char* argv[])
#endif
{
#ifdef _WIN32
_setmode(_fileno(stdout), _O_WTEXT);
#endif
if (argc != 2) {
cerr << "A URL is needed!\n";
return 1;
}
//等待请求及其关联处理全部完成
try {
auto request = get_request(argv[1]);
request.wait();
}
//处理请求过程中产生的异常
catch (const std::exception& e){
cerr << "Error exception " << e.what() << endl;
return 1;
}
}
C++REST SDK是用来开发http客户端和服务器的现代异步C++代码库,支持http服务器,http客户端,异步流,任务,JSON,URI,websocket客户端。
异步流
C++REST SDK实现了一套异步流,能够实现对文件的异步读写。
以下代码展示了把网络请求响应异步存储到文件result.html中
#include
#include
#ifdef _WIN32
#include
#include
#endif
#include
#include
#include
using namespace utility;
using namespace web::http;
using namespace web::http::client;
using namespace concurrency::streams;
using namespace std::cerr;
using namespace std::endl;
#ifdef _WIN32 //根据平台来定义tcout,确保多语言的文字能够正确输出
#define tcout std::wcout;
#else
#define tcout std::cout;
#endif
//从http_response中取出头部的字符串表示
auto get_headers(http_response resp) {
auto headers = resp.to_string();
auto end = headers.find("\r\n\r\n");
if (end != string_t::npos) {
headers.resize(end + 4);
}
return headers;
}
auto get_request(string_t uri) {
http_client client{
uri };
//用GET的方式发起一个客户端请求
//并使用then方法串联了两个下一步动作
//http_client::request的返回值是pplx::task.then是pplx::task类模板的成员函数,参数是能接受其类型参数对象的函数对象,
//除了最后一个then,其他每个then里都应该返回一个pplx::task,而task的内部类型,就是下一个then块里函数对象接受的参数类型。
auto request = client.request(methods::GET)
.then([](http_response resp) {
if (resp.status_code() == status_codes::OK) {
//正常的话
tcout << U("Saving ...\n");
ostream fs;
fstream::open_ostream(
U("result.html"), std::ios_base::out | std::ios_base::trunc
).then([&fs, resp](ostream os) {
fs = os;
//读取网页内容到流
return resp.body().read_to_end(fs.streambuf());
}).then([&fs](size_t size) {
fs.close();
tcout << size << U(" bytes saved\n");
}).wait();
}
else {
//否则显示当前响应信息
auto headers = get_headers(resp);
tcout << headers;
tcout << resp.extract_string().get();
}
});
return request;
}
#ifdef _WIN32 //根据平台定义合适的程序入口
int wmian(int argc, wchar_t* argv[])
#else
int main(int argc, char* argv[])
#endif
{
#ifdef _WIN32
_setmode(_fileno(stdout), _O_WTEXT);
#endif
if (argc != 2) {
cerr << "A URL is needed!\n";
return 1;
}
//等待请求及其关联处理全部完成
try {
auto request = get_request(argv[1]);
request.wait();
}
//处理请求过程中产生的异常
catch (const std::exception& e){
cerr << "Error exception " << e.what() << endl;
return 1;
}
}
C++REST SDK的对象大部分都是基于shared_ptr实现的,因而可以轻松大胆的进行复制
JSON支持
C++REST SDK对JSON有很好的支持,JSON的基本类型有空值类型,布尔类型,数字类型和字符串类型,复合类型是数组(array)和对象(object)
在C++REST SDK里,核心类型是web::json::value
C++REST SDK里的http_request 和http_response对JSON有原生支持,如可以使用extract_json成员函数来异步提取http请求或响应体中的JSON内容
http服务器
C++ REST SDK的http_listener会通过调用Boost.Asio和操作系统的底层接口(IOCP,epoll)来完成功能,向使用者隐藏这些细节,提供一个简单的编程接口
//简单rest服务器示例代码,处理sayHi请求
#include
#include
#include
#include
#ifdef _WIN32
#include
#include
#endif
#include
#include
using namespace std;
using namespace utility;
using namespace web;
using namespace web::http;
using namespace web::http::experimental::listener;
#ifdef _WIN32
#define tcout std::wcout;
#else
#define tcout std::cout;
#endif
void handle_get(http_request req) {
//http_request::request_uri函数返回的是uri的引用,因而用auto&来接收
auto& uri = req.request_uri();
if (uri.path() != U("/sayHi")) {
req.reply(status_codes::NotFound);
return;
}
tcout << uri::decode(uri.query()) << endl;
auto query = uri::split_query(uri.query());
auto it = query.find(U("name"));
if (it == query.end()) {
req.reply(status_codes::BadRequest, U("Missing query info"));
return;
}
//http_request::reply的第二个参数是json::value类型,这会让HTTP的Content-Type自动设置成 application/json
auto answer = json::value::object(true);
answer[U("msg")] = json::value(string_t(U("Hi, ")) + uri::decode(it->second) + U("!"));
req.reply(status_codes::OK, answer);
}
int main() {
#ifdef _WIN32
_setmode(_fileno(stdout), _O_WTEXT);
#endif
http_listener listener(U("http://127.0.0.1:8080/"));
listener.support(methods::GET, handle_get);
try {
listener.open().wait();
tcout << "Listener. Press Enter to exit.\n";
string line;
getline(cin, line);
listener.close().wait();
}
catch (const exception& e) {
cerr << e.what() << endl;
return 1;
}
}
C++REST SDK使用异步的编程模式,使得写不阻塞的代码变得很容易,底层它是用一个线程池实现的,缺省会开启40个线程,如果使用完了,会导致系统阻塞,线程数量代码中可控
//设置线程池大小为10
#include
crossplat::threadpool::initialize_with_threads(10);
C++REST服务器应当增加线程池大小,并且对并发数量进行统计,在并发数接近线程池大小时拒绝新的连接,一般可返回status_codes::ServiceUnavailable,以避免系统阻塞