I'm developing a web service to serve json objects to a jeasyui async tree. My HTML has the following:
<ul id="tt" method="POST" class="easyui-tree" url="http://w.x.y.z:1024/testrest">
</ul>
Assume w.x.y.z is my server's IP address. According to the jeasyui documentation for their PHP json service, I need to return an array of dictionary objects that have keys id
, text
, and state
. Okay, so far so good. I am attempting to develop a json service in c++ using the cpprest-sdk from Microsoft. I compiled and installed this library on RHEL 7.2 and am able to write some basic services using it. The problem lies (I think) with the encoding of the json that gets sent back to the client.
Here's a fully-functional example json server written with cpprest-sdk that handles POST requests and replies with a singly-populated array of dictionary objects that conform to the protocol expected by jeasyui:
#include <cpprest/http_listener.h>
#include <cpprest/json.h>
#pragma comment(lib, "cpprestlib" )
using namespace web;
using namespace web::http;
using namespace web::http::experimental::listener;
#include <iostream>
#include <map>
#include <set>
#include <string>
using namespace std;
#define TRACE(msg) wcout << msg
void handle_request(http_request request, function<void(const json::value &, json::value &, bool)> action)
{
json::value answer;
TRACE("\nHandle_request\n");
// Spit out the HTTP header to the console...
const auto HeaderString = request.to_string();
wcout << HeaderString.c_str() << endl;
request
.extract_json()
.then([&answer, &action](pplx::task<json::value> task) {
try
{
const auto & jvalue = task.get();
if (!jvalue.is_null())
{
action(jvalue, answer, false);
}
else
{
action(jvalue, answer, true);
}
}
catch (http_exception const & e)
{
wcout << "HTTP exception in handle_request: " << e.what() << endl;
}
})
.wait();
request.reply(status_codes::OK, answer);
}
void handle_post(http_request request)
{
TRACE("\nHandle POST\n");
handle_request(
request,
[](const json::value & jvalue, json::value & answer, bool bNull)
{
const utility::string_t sID("id");
const utility::string_t sText("text");
const utility::string_t sState("state");
if( bNull )
{
wcout << "jvalue must be null, setting some default values..." << endl;
json::value group;
group[sID] = json::value::string("1");
group[sText] = json::value::string("Hello");
group[sState] = json::value::string("closed");
answer[0] = group;
}
else
{
// To be written once the null case is sorted
}
}
);
}
int main()
{
uri_builder uri("http://w.x.y.z:1024/testrest");
http_listener listener(uri.to_uri());
listener.support(methods::POST, handle_post);
try
{
listener
.open()
.then([&listener]()
{
TRACE(L"\nStarting to listen\n");
})
.wait();
while (true);
}
catch (exception const & e)
{
wcout << e.what() << endl;
}
return 0;
}
This compiles cleanly and I can start the service on the linux server with the following:
./testrest &
Starting to listen
To aid in debugging, I've been using curl
to serve as a POST client directly on the same linux server. I've been using the following command to send a POST request with 0 content-length:
curl -i -X POST -H 'Content-Type: application/json' http://w.x.y.z:1024/testrest
The output from curl is the following:
HTTP/1.1 200 OK
Content-Length: 44
Content-Type: application/json
[{"id":"1","state":"closed","text":"Hello"}]
and the console messages from my service are as such:
Handle POST
Handle_request
POST /testrest HTTP/1.1
Accept: */*
Content-Type: application/json
Host: w.x.y.z:1024
User-Agent: curl/7.29.0
jvalue must be null, setting some default values...
The first two lines correspond to the TRACE
calls in the code. The middle section is generated by this section of code:
// Spit out the HTTP header to the console...
const auto HeaderString = request.to_string();
wcout << HeaderString.c_str() << endl;
Based on the curl output, which is an array of dictionary objects exactly one entry long, I would expect that this service should work just fine with the jeasyui javascript on the client. However, it does not. My async tree never populates and I don't see anything at all.
I suspect there's something wrong with the encoding, and so I wrote another service using web2py to test to see if it would work there. The following code exists in my default.py controller:
@service.json
def testweb2py():
aRet=[]
if request.post_vars.id is None:
mydict={'id':'1','text':'Hello','state':'closed'}
aRet.append(mydict)
return aRet
after modifying my client easyui-tree
HTML to point to the web2py URL, it populates perfectly and I can see the node. I hit the web2py service.json code with curl just to see how the output might differ:
HTTP/1.1 200 OK
Date: Mon, 23 Jan 2017 18:17:17 GMT
Server: Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_wsgi/3.4 Python/2.7.5
X-Powered-By: web2py
Expires: Mon, 23 Jan 2017 18:17:18 GMT
Pragma: no-cache
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Length: 99
Content-Type: application/json; charset=utf-8
[{"text": "Hello", "state": "closed", "id": "1"}]
Aside from the content header being quite different, there's one line that I suspect might have something to do with it:
Content-Type: application/json; charset=utf-8
In the call to the cpprest service, the header output from curl does not include charset=utf-8
in it. If I dump the curl output to a file using the -o
switch, I don't see any clear difference between the encoding. The only thing that I can see different in the format of the json is some extra whitespace and the ordering:
[{"text": "Hello", "state": "closed", "id": "1"}] // web2py version
[{"id":"1","state":"closed","text":"Hello"}] // cpprest version
I'm unable to gain any control over the order in which the json dictionary is sent, but I doubt that has anything to do with it anyways. The extra whitespace prefixing the value entry seems irrelevant as well.
I've poured over the cpprest documentation over at microsoft.github.io/cpprestsdk/index.html,
and I cannot find anything that relates to setting the output encoding. There are a number of overrides to http_request::reply
that include options for setting content-type, and I've gone down the road of calling them with hard-coded strings for both the json body and the content-type of json/application; charset=utf-8
, all to no avail. I don't see how those overrides can be used with json::value objects at any rate, so I don't think that's the optimal path or a viable use of this cpprest library.
The jeasyui javascript code appears to be intentionally obfuscated, and I have little faith in being able to figure out what it is doing with the reply from the POST call. Maybe someone familiar with jeasyui can point to a viable means for debugging the async POST?
Please help!