'Boost.beast : how to return json response only
code : https://gist.github.com/Naseefabu/173c928603e564879683ccdf10d9d0f8
When i run this and print the response to the console:
Sending GET /api/v3/time HTTP/1.1
Host: api.binance.com
content-type: application/json
User-Agent: Boost.Beast/330
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 28
Connection: keep-alive
Date: Fri, 13 May 2022 17:18:47 GMT
Server: nginx
x-mbx-uuid: 7e7465db-011a-4308-aa1e-6603d72c8c9a
x-mbx-used-weight: 1
x-mbx-used-weight-1m: 1
Strict-Transport-Security: max-age=31536000; includeSubdomains
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'self'
X-Content-Security-Policy: default-src 'self'
X-WebKit-CSP: default-src 'self'
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, HEAD, OPTIONS
X-Cache: Miss from cloudfront
Via: 1.1 dfccb338f8c0489ab09835ea7dbad1a8.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: MAA51-P1
X-Amz-Cf-Id: waTqtDHSDpk74QB7zkF5Ya0CdRVWuJuC-M4TZqSuMd2bfXawkq6o6g==
{"serverTime":1652462327804}
shutdown: stream truncated
what if i just want to get the json response : {"serverTime":1652462327804}
?
and store in the json variable so i could use it for my needs, other informations is not that important for me, advance thanks!
Solution 1:[1]
binapi::AsyncRest::httpClient* client;
That's extremely suspect, since the class is using enable_shared_from_this()
. Pretty sure that should be
auto client =
std::make_shared<binapi::AsyncRest::httpClient>(ioc.get_executor(), ctx);
Next up, I assume get_server_time
is a static function. I don't see why it is a member of httpClient
.
So adding all the missing code back in (using lots of experience):
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/url.hpp>
#include <boost/url/src.hpp> // for header-only
#include <fstream>
#include <iomanip>
#include <iostream>
namespace net = boost::asio;
namespace beast = boost::beast;
namespace http = boost::beast::http;
namespace ssl = boost::asio::ssl;
using net::ip::tcp;
namespace binapi { namespace AsyncRest {
// Report a failure
void fail_http(beast::error_code ec, char const* what) {
std::cerr << what << ": " << ec.message() << "\n";
}
struct httpClient : std::enable_shared_from_this<httpClient> {
using executor = net::any_io_executor;
using Stream = beast::ssl_stream<beast::tcp_stream>;
tcp::resolver resolver_;
Stream stream_;
beast::flat_buffer buffer_;
http::request<http::empty_body> req_;
http::response<http::string_body> res_;
httpClient(executor ex, ssl::context& ctx);
// Start the asynchronous operation
void run(boost::url, http::verb);
void on_resolve(beast::error_code, tcp::resolver::results_type);
void on_connect(beast::error_code, tcp::resolver::results_type::endpoint_type);
void on_handshake(beast::error_code);
void on_write(beast::error_code, size_t bytes_transferred);
void on_read(beast::error_code, size_t bytes_transferred);
void on_shutdown(beast::error_code);
};
httpClient::httpClient(executor ex, ssl::context& ctx)
: resolver_(ex)
, stream_(ex, ctx) {}
// Start the asynchronous operation
void httpClient::run(boost::url url, http::verb action) {
std::string const host(url.host());
std::string const service = url.has_port() //
? url.port()
: (url.scheme_id() == boost::urls::scheme::https) //
? "https"
: "http";
url.remove_origin(); // becomes req_.target()
// Set SNI Hostname (many hosts need this to handshake
// successfully)
if (!SSL_set_tlsext_host_name(stream_.native_handle(), host.c_str())) {
beast::error_code ec{static_cast<int>(::ERR_get_error()),
net::error::get_ssl_category()};
std::cerr << ec.message() << "\n";
return;
}
// Set up an HTTP GET/POST/DELETE/PUT request message
// req_.version(version);
req_.method(action);
req_.target(url.c_str());
req_.set(http::field::host, host);
req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
req_.prepare_payload(); // make HTTP 1.1 compliant
// Look up the domain name
resolver_.async_resolve(
host, service,
beast::bind_front_handler(&httpClient::on_resolve, shared_from_this()));
}
void httpClient::on_resolve(beast::error_code ec,
tcp::resolver::results_type results) {
if (ec)
return fail_http(ec, "resolve");
// Set a timeout on the operation
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(stream_).async_connect(
results,
beast::bind_front_handler(&httpClient::on_connect, shared_from_this()));
}
void httpClient::on_connect(beast::error_code ec,
tcp::resolver::results_type::endpoint_type) {
if (ec)
return fail_http(ec, "connect");
// Perform the SSL handshake
stream_.async_handshake(
ssl::stream_base::client,
beast::bind_front_handler(&httpClient::on_handshake, shared_from_this()));
}
void httpClient::on_handshake(beast::error_code ec) {
if (ec)
return fail_http(ec, "handshake");
// Set a timeout on the operation
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
// Send the HTTP request to the remote host
std::cout << "Sending " << req_ << std::endl;
http::async_write(
stream_, req_,
beast::bind_front_handler(&httpClient::on_write, shared_from_this()));
}
void httpClient::on_write(beast::error_code ec, size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);
if (ec)
return fail_http(ec, "write");
// Receive the HTTP response
http::async_read(
stream_, buffer_, res_,
beast::bind_front_handler(&httpClient::on_read, shared_from_this()));
}
void httpClient::on_read(beast::error_code ec, size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);
if (ec)
return fail_http(ec, "read");
// Write the message to standard out
std::cout << res_ << std::endl;
// Set a timeout on the operation
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
// Gracefully close the stream
stream_.async_shutdown(
beast::bind_front_handler(&httpClient::on_shutdown, shared_from_this()));
}
void httpClient::on_shutdown(beast::error_code ec) {
if (ec == net::error::eof) {
ec = {};
}
if (ec)
return fail_http(ec, "shutdown");
}
static void get_server_time(net::io_context& ioc, ssl::context& ctx) {
static boost::url_view const uri{"https://api.binance.com/api/v3/time"};
std::make_shared<httpClient>(net::make_strand(ioc), ctx)
->run(uri, http::verb::get);
}
}} // namespace binapi::AsyncRest
int main() {
net::io_context ioc;
// The SSL context is required, and holds certificates
ssl::context ctx{ssl::context::tlsv12_client};
// Verify the remote server's certificate
ctx.set_verify_mode(ssl::verify_peer);
ctx.set_default_verify_paths();
binapi::AsyncRest::get_server_time(ioc, ctx);
ioc.run();
}
Now we know that res_
is beast::http::response<beast::http::string_body>
. So, if you only want to print the body, print that:
std::cout << res_.body() << std::endl;
Prints
Sending GET /api/v3/time HTTP/1.1
Host: api.binance.com
User-Agent: Boost.Beast/330
{"serverTime":1652476115413}
shutdown: stream truncated
To only print the time:
static constexpr boost::gregorian::date s_epoch{1970, 1, 1};
auto epoch_seconds = json::parse(res_.body()).at("serverTime").as_int64();
ptime serverTime(s_epoch, boost::posix_time::milliseconds(epoch_seconds));
std::cout << serverTime << std::endl;
Prints
2022-May-13 21:38:55.982000
Summary/Notes
What it really looks like you're after is a function like
ptime server_time();
Or
void async_server_time(auto completionToken);
And you'd probably want to share the client class instance instead of reconnecting for each call.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | sehe |