1、响应对象 (Response Objects)
Response对象是数据包。他们的唯一目的是在请求结束时提供客户端信息 - 在API Response返回给您之后,没有任何API使用它。这个推理促使决定使响应的成员字段公开和可变。
一个Response有一下几个领域:
long status_code; // The HTTP status code for the request
std::string text; // The body of the HTTP response
Header header; // A map-like collection of the header fields
Url url; // The effective URL of the ultimate request
double elapsed; // The total time of the request in seconds
Cookies cookies; // A map-like collection of cookies returned in the request
而且访问非常简单:
auto r = cpr::Get(cpr::Url{"http://www.httpbin.org/get"});
if (r.status_code >= 400) {
std::cerr << "Error [" << r.status_code << "] making request" << std::endl;
} else {
std::cout << "Request took " << r.elapsed << std::endl;
std::cout << "Body:" << std::endl << r.text;
}
这Header本质上是一个具有重要修改的地图。按照RFC 7230的要求,其密钥不区分大小写:
auto r = cpr::Get(cpr::Url{"http://www.httpbin.org/get"});
std::cout << r.header["content-type"] << std::endl;
std::cout << r.header["Content-Type"] << std::endl;
std::cout << r.header["CoNtEnT-tYpE"] << std::endl;
所有这些应该打印相同的值”application/json”。类似地,Cookie也可以通过类似地图的界面访问,但它们不区分大小写:
auto r = cpr::Get(cpr::Url{"http://www.httpbin.org/cookies/set?cookies=yummy"});
std::cout << r.cookies["cookies"] << std::endl; // Prints yummy
std::cout << r.cookies["Cookies"] << std::endl; // Prints nothing
正如你所看到的,这个Response对象是完全透明的。它的所有数据字段都可以随时访问,因为它只有在有信息可以沟通的时候才有用,所以当你完成它时,可以让它安全地超出范围。
2、请求标题 (Request Headers)
说到这个Header,你可以在请求调用中设置自定义标题。该对象完全相同:
auto r = cpr::Get(cpr::Url{"http://www.httpbin.org/headers"},
cpr::Header{{"accept", "application/json"}});
std::cout << r.text << std::endl;
/*
* "headers": {
* "Accept": "application/json",
* "Host": "www.httpbin.org",
* "User-Agent": "curl/7.42.0-DEV"
* }
*/
您可能已经注意到之间的相似性Header,Parameters,Payload,和Multipart。他们都有以下形式的构造函数:
auto header = cpr::Header{{"header-key", "header-value"}};
auto parameters = cpr::Parameters{{"parameter-key", "parameter-value"}};
auto payload = cpr::Payload{{"payload-key", "payload-value"}};
auto multipart = cpr::Multipart{{"multipart-key", "multipart-value"}};
这不是偶然的 - 所有这些都是类地图对象,它们的语法是相同的,因为它们的语义完全取决于对象类型。此外,它的实用性有Parameters,Payload和Multipart是可交换的,因为有时的API没有严格区分它们。
3、会话对象 (Session Objects)
在引擎盖下,Session在执行请求之前,所有对主API的调用都会修改名为a的对象。这是图书馆唯一真正有状态的一部分,对于大多数应用程序来说,没有必要Session直接采取行动,宁愿让图书馆为你处理。
但是,如果持有状态有用,可以使用Session:
auto url = cpr::Url{"http://www.httpbin.org/get"};
auto parameters = cpr::Parameters{{"hello", "world"}};
cpr::Session session;
session.SetUrl(url);
session.SetParameters(parameters);
auto r = session.Get(); // Equivalent to cpr::Get(url, parameters);
std::cout << r.url << std::endl // Prints http://www.httpbin.org/get?hello=world
auto new_parameters = cpr::Parameters{{"key", "value"}};
session.SetParameters(new_parameters);
auto new_r = session.Get(); // Equivalent to cpr::Get(url, new_parameters);
std::cout << new_r.url << std::endl // Prints http://www.httpbin.org/get?key=value
Session实际上公开了两个不同的界面来设置相同的选项。如果你想要,你可以做到这一点,而不是上述:
auto url = cpr::Url{"http://www.httpbin.org/get"};
auto parameters = cpr::Parameters{{"hello", "world"}};
cpr::Session session;
session.SetOption(url);
session.SetOption(parameters);
auto r = session.Get();
这很重要,所以它强调:对于每个配置选项(如Url,Parameters),都有相应的方法Set和aSetOption()。第二个接口是为了促进模板元编程魔术,让API暴露无序方法。
所有这一切的关键实际上就是libcurl的设计方式。它使用基于策略的设计,依赖于配置单个库对象(curl句柄)。配置到该对象中的每个选项都以大部分正交的方式更改其行为。
Session利用它并公开一个更现代化的界面,免除了libcurl巨大的浪费。了解基于策略的libcurl设计对于理解Session对象的行为方式非常重要。
4、异步请求 (Asynchronous Requests)
制作异步请求使用类似但独立的接口:
auto fr = cpr::GetAsync(cpr::Url{"http://www.httpbin.org/get"});
// Sometime later
auto r = fr.get(); // This blocks until the request is complete
std::cout << r.text << std::endl;
电话是除,而不是其他相同的Get,它是GetAsync。与POST请求类似,您可以调用PostAsync。异步调用的返回值实际上是一个std::future<Response>
:
auto fr = cpr::GetAsync(cpr::Url{"http://www.httpbin.org/get"});
fr.wait() // This waits until the request is complete
auto r = fr.get(); // Since the request is complete, this returns immediately
std::cout << r.text << std::endl;
你甚至可以把一堆请求放入一个std容器中,并在以后使用它们:
auto container = std::vector<std::future<cpr::Response>>{};
auto url = cpr::Url{"http://www.httpbin.org/get"};
for (int i = 0; i < 10; ++i) {
container.emplace_back(cpr::GetAsync(url, cpr::Parameters{{"i", std::to_string(i)}}));
}
// Sometime later
for (auto& fr: container) {
auto r = fr.get();
std::cout << r.text << std::endl;
}
这里要做的一个重要的注意事项是传递给异步调用的参数被复制。在引擎盖下,通过库的API的异步调用完成std::async。默认情况下,为了内存安全,所有参数都被复制(或者在临时移动时),因为没有语法级别保证参数超出请求的范围。
可以强制std::async使用此默认值,以便参数传递参数而不是值。然而,cpr::Async目前不支持强制传递,尽管这是为将来的版本计划的。
5、异步回调(Asynchronous Callbacks)
C ++请求还支持异步请求的回调接口。使用回调接口,传递一个仿函数(lambda,函数指针等)作为第一个参数,然后传入其他通常在阻塞请求中选择的选项。函子需要有一个参数,一个Response对象 - 当请求完成并且函数体执行时,这个响应被填充。
这里有一个简单的例子:
auto future_text = cpr::GetCallback([](cpr::Response r) {
return r.text;
}, cpr::Url{"http://www.httpbin.org/get"});
// Sometime later
if (future_text.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
std::cout << future_text.get() << std::endl;
}
这里有几个关键特性需要指出:
1. 返回值是a std::string。这不是硬编码的 - 回调可以自由返回任何值!当需要获取该值时,检查请求是否完成,然后简单地.get()调用将来的值来获取正确的值。这种灵活性使回调界面非常简单,通用且高效!
2. lambda捕获是空的,但绝对不需要。任何可以在lambda中捕获的东西通常都可以在传入回调接口的lambda中捕获。这种额外的灵活性向量使得它非常适合使用lambda表达式,尽管任何具有Response参数的函子都可以编译和工作。
另外,你可以Response用const Response&参数而不是简单地强制执行不变性Response。
6、设置超时 (Setting Timeout)
如果您有严格的时间要求,可以为您的请求设置超时时间:
#include <assert.h>
auto r = cpr::Get(cpr::Url{"http://www.httpbin.org/get"},
cpr::Timeout{1000}); // Let's hope we aren't using Time Warner Cable
assert(r.elapsed <= 1); // Less than one second should have elapsed
设置Timeout选项可设置传输操作可以承受的最大时间。由于C ++请求是建立在libcurl的基础上的,因此了解Timeout对请求做了什么设置很重要。你可以在这里找到更多关于特定libcurl选项的信息。
7、使用代理 (Using Proxies)
Proxies,就像Parameters是地图式的对象。设置一个很容易:
auto r = cpr::Get(cpr::Url{"http://www.httpbin.org/get"},
cpr::Proxies{{"http", "http://www.fakeproxy.com"}});
std::cout << r.url << std::endl; // Prints http://www.httpbin.org/get, not the proxy url
它看起来并不Proxies像一张地图那样有用,但在使用Session它时更明显:
cpr::Session session;
session.SetProxies({{"http", "http://www.fakeproxy.com"},
{"https", "http://www.anotherproxy.com"}})
session.SetUrl("http://www.httpbin.org/get");
{
auto r = session.Get();
std::cout << r.url << std::endl; // Prints http://wcww.httpbin.org/get after going
// through http://www.fakeproxy.com
}
session.SetUrl("https://www.httpbin.org/get");
{
auto r = session.Get();
std::cout << r.url << std::endl; // Prints https://www.httpbin.org/get after going
// through http://www.anotherproxy.com
}
设置Proxies一个Session可以让你通过使用不同的代理不同协议智能路由请求,而无需respecify什么,但该请求Url。
8、发送Cookies (SendingCookies)
之前您看到了如何从请求中获取cookie:
auto r = cpr::Get(cpr::Url{"http://www.httpbin.org/cookies/set?cookies=yummy"});
std::cout << r.cookies["cookies"] << std::endl; // Prints yummy
std::cout << r.cookies["Cookies"] << std::endl; // Prints nothing
您可以使用同一个对象发回Cookie:
auto r = cpr::Get(cpr::Url{"http://www.httpbin.org/cookies/set?cookies=yummy"});
auto another_r = cpr::Get(cpr::Url{"http://www.httpbin.org/cookies"}, r.cookies);
std::cout << another_r.text << std::endl;
/*
* {
* "cookies": {
* "cookie": "yummy"
* }
* }
*/
这是特别有用的,因为Cookies通常从服务器到客户端并返回到服务器。设置新的Cookies应该不会令人惊讶:
auto r = cpr::Get(cpr::Url{"http://www.httpbin.org/cookies"},
cpr::Cookies{{"ice cream", "is delicious"}});
std::cout << r.text << std::endl;
/*
* {
* "cookies": {
* "ice%20cream": "is%20delicious"
* }
* }
*/
请注意,按照RFC 2965的要求,cookie是如何使用url编码模式进行编码的。除了这个怪癖,使用Cookies相当简单,只是工作。
9、PUT和PATCH请求(PUT and PATCH Requests)
PUT和PATCH请求与POST请求的工作方式相同,唯一的修改是指定的HTTP方法是”PUT”或”PATCH”不是”POST”。当你调用的API的语义实现这些请求的特殊行为时使用它:
#include <assert.h>
// We can't POST to the "/put" endpoint so the status code is rightly 405
assert(cpr::Post(cpr::Url{"http://www.httpbin.org/put"},
cpr::Payload{{"key", "value"}}).status_code == 405);
// On the other hand, this works just fine
auto r = cpr::Put(cpr::Url{"http://www.httpbin.org/put"},
cpr::Payload{{"key", "value"}});
std::cout << r.text << std::endl;
/*
* {
* "args": {},
* "data": "",
* "files": {},
* "form": {
* "key": "value"
* },
* "headers": {
* ..
* "Content-Type": "application/x-www-form-urlencoded",
* ..
* },
* "json": null,
* "url": "https://httpbin.org/put"
* }
*/
大多数情况下,PUT用于使用新对象更新现有对象。当然,这并不能保证任何特定的API以这种方式使用PUT语义,所以只有在它有意义时才使用它。这是一个示例PATCH请求,它基本相同:
#include <assert.h>
// We can't POST or PUT to the "/patch" endpoint so the status code is rightly 405
assert(cpr::Post(cpr::Url{"http://www.httpbin.org/patch"},
cpr::Payload{{"key", "value"}}).status_code == 405);
assert(cpr::Put(cpr::Url{"http://www.httpbin.org/patch"},
cpr::Payload{{"key", "value"}}).status_code == 405);
// On the other hand, this works just fine
auto r = cpr::Patch(cpr::Url{"http://www.httpbin.org/patch"},
cpr::Payload{{"key", "value"}});
std::cout << r.text << std::endl;
/*
* {
* "args": {},
* "data": "",
* "files": {},
* "form": {
* "key": "value"
* },
* "headers": {
* ..
* "Content-Type": "application/x-www-form-urlencoded",
* ..
* },
* "json": null,
* "url": "https://httpbin.org/patch"
* }
*/
和PUT一样,只有当您发送请求的API支持该方法时,PATCH才有效。
其他请求方法
C ++请求还支持DELETE,HEAD以及OPTIONS在预期的形式的方法:
// Regular, blocking modes
auto delete_response = cpr::Delete(cpr::Url{"http://www.httpbin.org/delete"});
auto head_response = cpr::Head(cpr::Url{"http://www.httpbin.org/get"});
auto options_response = cpr::OPTIONS(cpr::Url{"http://www.httpbin.org/get"});
// Asynchronous, future mode
auto async_delete_response = cpr::DeleteAsync(cpr::Url{"http://www.httpbin.org/delete"});
auto async_head_response = cpr::HeadAsync(cpr::Url{"http://www.httpbin.org/get"});
auto async_options_response = cpr::OptionsAsync(cpr::Url{"http://www.httpbin.org/get"});
// Asynchronous, callback mode
auto cb_delete_response = cpr::DeleteCallback([](cpr::Response r) {
return r.text;
}, cpr::Url{"http://www.httpbin.org/delete"});
auto cb_head_response = cpr::HeadCallback([](cpr::Response r) {
return r.status_code;
}, cpr::Url{"http://www.httpbin.org/get"});
auto cb_options_response = cpr::OptionsCallback([](cpr::Response r) {
return r.status_code;
}, cpr::Url{"http://www.httpbin.org/get"});
目前,”PATCH”不是一种实施的HTTP方法。它很快就会成立,其机制将与上述例子相一致。