C++ Consume API REST with Casablanca and POST XML - c++

The question is how to consume API REST that needs to POST content on body type XML and on headers parameters. I found only solutions with JSON.
return pplx::create_task([]
{
json::value postData;
postData[L"name"] = json::value::string(L"Joe Smith");
postData[L"hobby"] = json::value::string(L"Baseball");
http_client client(L"http://localhost:5540/api/values");
return client.request(methods::POST, L"",
postData.to_string().c_str(), L"application/xml");
}).then([](http_response response)
{
if (response.status_code() == status_codes::OK)
{
auto body = response.extract_string();
std::wcout << L"Added new Id: " << body.get().c_str() << std::endl;
return std::stoi(body.get().c_str());
}
return 0;
});
Thanks for all!

I changed the library. Using libcurl with no problem and it's easier.

Related

How can I issue a POST request that contains a basic authentication header, and a JSON body?

I am trying to use the CPPRESTSDK (a.k.a. Casablanca) to POST data to a RESTful server. To do this, I create a request, and assign a header:
// create request, and add header information
web::http::http_request req(methods::POST);
req.headers().add(header_names::authorization, authStr); // authStr is base64 representation of username & password
req.headers().add(header_names::content_type, http::details::mime_types::application_json);
Next, I make a web::json::value object that contains all the key-value pairs:
web::json::value obj = json::value::object();
obj[U("Key1")] = web::json::value::string(U("Val1")];
obj[U("Key2")] = web::json::value::string(U("Val2")];
obj[U("Key3")] = web::json::value::string(U("Val3")];
I then store this object in the request's body by calling:
req.set_body(obj);
Finally, I send the request to the server using an http_client:
// create http client
web::http::client::http_client client(addr); // addr is wstring
return client.request(req).then([](http_response response) {
return response;
});
The problem is that this doesn't do anything. If I place a breakpoint on this line, I get information about "400 Bad Request." I would assume that the request's body is somehow malformed, but it could also be that I am missing some information in the header. This error does not happen when I issue a GET request on the same URL, so it is definitely a problem with POSTs specifically. What do you think?
Here is a working example:
// create a new channel
pplx::task<web::http::http_response> postChannel(http_client client, std::wstring authStr, std::wstring cDesc, std::wstring cName, std::string cDiagCap, int cNormFloat, int cWriteDuty,
int cWriteMeth, std::string cItemPersist, std::wstring cItemPersistDat) {
// create request
http_request req(methods::POST);
req.headers().add(header_names::authorization, authStr);
std::wstring url = L"/config/v1/project/channels";
req.set_request_uri(url);
json::value obj = json::value::object();
obj[U("common.ALLTYPES_DESCRIPTION")] = json::value::string(cDesc);
obj[U("common.ALLTYPES_NAME")] = json::value::string(cName);
obj[U("servermain.CHANNEL_DIAGNOSTICS_CAPTURE")] = json::value(cDiagCap == "true" || cDiagCap == "t");
obj[U("servermain.CHANNEL_NON_NORMALIZED_FLOATING_POINT_HANDLING")] = json::value(cNormFloat);
obj[U("servermain.CHANNEL_WRITE_OPTIMIZATIONS_DUTY_CYCLE")] = json::value(cWriteDuty);
obj[U("servermain.CHANNEL_WRITE_OPTIMIZATIONS_METHOD")] = json::value(cWriteMeth);
obj[U("servermain.MULTIPLE_TYPES_DEVICE_DRIVER")] = json::value::string(U("Simulator")); // right now, Simulator channels are the only option
obj[U("simulator.CHANNEL_ITEM_PERSISTENCE")] = json::value(cItemPersist == "true" || cItemPersist == "t");
obj[U("simulator.CHANNEL_ITEM_PERSISTENCE_DATA_FILE")] = json::value::string(cItemPersistDat);
req.set_body(obj);
return client.request(req).then([](http_response response) {
return response;
});
}

cpprestsdk: handle chunked response

How should I handle chunked response using cpprestsdk? How to request the next chunk? Is there required functionality at all there?
Here is how we are performing http requests:
web::http::http_request request(web::http::methods::GET);
request.headers().add(LR"(User-Agent)", LR"(ExchangeServicesClient/15.00.0847.030)");
request.headers().add(LR"(Accept)", LR"(text/xml)");
request.set_body(L"request body", L"text/xml");
web::http::client::http_client_config clientConfig;
clientConfig.set_credentials(web::credentials(L"username", L"pass"));
clientConfig.set_validate_certificates(true);
web::http::client::http_client client(L"serviceurl", clientConfig);
auto bodyTask = client.request(request)
.then([](web::http::http_response response) {
auto str = response.extract_string().get();
return str;
});
auto body = bodyTask.get();
If I'm trying naively to perform another request just after this one then I got an error:
WinHttpSendRequest: 5023: The group or resource is not in the correct state to perform the
requested operation.
In order to read received data in chunks, one needs to get the input stream from the server response
concurrency::streams::istream bodyStream = response.body();
then read continuously from that stream until a given char is found or the number of specified bytes is read
pplx::task<void> repeat(Concurrency::streams::istream bodyStream)
{
Concurrency::streams::container_buffer<std::string> buffer;
return pplx::create_task([=] {
auto t = bodyStream.read_to_delim(buffer, '\n').get();
std::cout << buffer.collection() << std::endl;
return t;
}).then([=](int /*bytesRead*/) {
if (bodyStream.is_eof()) {
return pplx::create_task([]{});
}
return repeat(bodyStream);
});
}
Here is the full sample: https://github.com/cristeab/oanda_stream

Not able to get values from JSON in Casablanca, C++

I'm using Casablanca, cpprestsdk to consume REST APIs in C++, in Visual Studio 2015 Professional. I'm trying to develop a simple example here hitting an API and parsing the response as JSON. The URL I'm using, actually returns all the parameters sent to the API.
I've hit the API and got response as well, extracted json from the response successfully. But when i try to read a value at any key from json, it crashes. Hence i put a check whether that key is available or not, and it always says json does not have the field. As an example i printed the data i.e. json. It has the key/field "name" but when i check it via has_field, it returns false.
Please help.
Complete code is below :
json::value postData;
postData[L"name"] = json::value::string(L"Joe Smith");
postData[L"sport"] = json::value::string(L"Baseball");
http_client client(L"https://httpbin.org/post);
http_request request(methods::POST);
request.set_body(postData);
client.request(request).then([](web::http::http_response response) {
json::value j = response.extract_json().get();
json::value data = j.at(U("data"));
std::wcout << "Json : " << data;
// Prints "{\"name\":\"Joe Smith\",\"sport\":\"Baseball\"}"
if (data.has_field(U("name"))) {
std::cout << "Name Found";
}
else {
std::cout << "Name key not Found";
}
});
It seems that your response looks like this:
{ "data": "{\"name\":\"Joe Smith\",\"sport\":\"Baseball\"}" }`
i.e. the actual data is not a JSon object but escaped JSon passed as string. I guess you need to return a payload that looks like this to do what you want to do the way you are doing it:
{
"data": {
"name": "John Smith",
"sport": "Baseball"
}
}

C++ rest sdk POST form data json

Is it possible to post "form data" whith C++ rest SDK (Casablanca)? I have a given web service which looking for post data in "form data", not in the body.
This is the C++ code:
http_client client(L"http://localhost/posttest/jsontest.php");
// Manually build up an HTTP request with header and request URI.
http_request request(methods::POST);
request.headers().add(L"Content-Type", L"application/json");
request.headers().add(L"Content-Length", L"100");
request.headers().add(L"Host", L"example.com");
request.headers().add(L"X-Requested-With", L"XMLHttpRequest");
request.set_body(obj);
return client.request(request).then([id](http_response response)
{
if (response.status_code() == status_codes::OK)
{
return response.extract_json();
}
else {
/* Print bad status code */
wcout << L"Server returned returned status code " << response.status_code() << L'.' << std::endl;
}
return pplx::task_from_result(json::value());
})
The web service can only use data like this (I can't modify it):
$arr = [$_POST['code']];
header('Content-Type: application/json');
echo json_encode($arr);
(This is just a sample PHP code, what I use for testing)
That is the way:
utility::string_t Lreq = L"code=" + Lcode;
http_client client(L"http://localhost/posttest/jsontest.php");
// Manually build up an HTTP request with header and request URI.
http_request request(methods::POST);
request.headers().add(L"Content-Type", L"application/x-www-form-urlencoded; charset=UTF-8");
request.headers().add(L"Content-Length", L"100");
request.headers().add(L"Host", L"testhost.com");
request.headers().add(L"X-Requested-With", L"XMLHttpRequest");
request.set_body(Lreq);

Why does SQL Server CLR procedure hang in GetResponse() call to web service

Environment: C#, .Net 3.5, Sql Server 2005
I have a method that works in a stand-alone C# console application project. It creates an XMLElement from data in the database and uses a private method to send it to a web service on our local network. When run from VS in this test project, it runs in < 5 seconds.
I copied the class into a CLR project, built it, and installed it in SQL Server (WITH PERMISSION_SET = EXTERNAL_ACCESS). The only difference is the SqlContext.Pipe.Send() calls that I added for debugging.
I am testing it by using an EXECUTE command one stored procedure (in the CLR) from an SSMS query window. It never returns. When I stop execution of the call after a minute, the last thing displayed is "Calling GetResponse() using http://servername:53694/odata.svc/Customers/". Any ideas as to why the GetResponse() call doesn't return when executing within SQL Server?
private static string SendPost(XElement entry, SqlString url, SqlString entityName)
{
// Send the HTTP request
string serviceURL = url.ToString() + entityName.ToString() + "/";
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(serviceURL);
request.Method = "POST";
request.Accept = "application/atom+xml,application/xml";
request.ContentType = "application/atom+xml";
request.Timeout = 20000;
request.Proxy = null;
using (var writer = XmlWriter.Create(request.GetRequestStream()))
{
entry.WriteTo(writer);
}
try
{
SqlContext.Pipe.Send("Calling GetResponse() using " + request.RequestUri);
WebResponse response = request.GetResponse();
SqlContext.Pipe.Send("Back from GetResponse()");
/*
string feedData = string.Empty;
Stream stream = response.GetResponseStream();
using (StreamReader streamReader = new StreamReader(stream))
{
feedData = streamReader.ReadToEnd();
}
*/
HttpStatusCode StatusCode = ((HttpWebResponse)response).StatusCode;
response.Close();
if (StatusCode == HttpStatusCode.Created /* 201 */ )
{
return "Created # Location= " + response.Headers["Location"];
}
return "Creation failed; StatusCode=" + StatusCode.ToString();
}
catch (WebException ex)
{
return ex.Message.ToString();
}
finally
{
if (request != null)
request.Abort();
}
}
The problem turned out to be the creation of the request content from the XML. The original:
using (var writer = XmlWriter.Create(request.GetRequestStream()))
{
entry.WriteTo(writer);
}
The working replacement:
using (Stream requestStream = request.GetRequestStream())
{
using (var writer = XmlWriter.Create(requestStream))
{
entry.WriteTo(writer);
}
}
You need to dispose the WebResponse. Otherwise, after a few calls it goes to timeout.
You are asking for trouble doing this in the CLR. And you say you are calling this from a trigger? This belongs in the application tier.
Stuff like this is why when the CLR functionality came out, DBAs were very concerned about how it would be misused.