HMRC MTD Hello World with Qt - json

I have a Qt program that stores all my small (tiny) company information on a sql database and I have over the years tailored it to do all my accounting stuff, invoices, BOMs etc.
At the push of a button I can get all of the necessary sql data to calculate a quarterly VAT return, but we're going to have to electronically submit all the data now, not just calculate it. I have all the data needed, it's just a case of submitting over HTTP using json (of which I know a little/nothing about respectively).
I'm small enough so that I don't have to do this submission at the moment, but the time will likely come, so I'm trying the most basic of requests in the HMRC's sandbox as a starting point.
On this page it shows you how to do an hello world request in Java, so I'm trying to do the same with Qt with C++.
I've tried the following which responds to the push of a button and I have of course, set up a slot to deal with a response:
void MainWindow::hello()
{
QJsonObject json;
QString rs("https://test-api.service.hmrc.gov.uk/hello/world");
QNetworkRequest request
{
QUrl(rs)
};
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/vnd.hmrc.1.0+json");
request.setUrl(QUrl(rs));
manager->get(request);
}
and the main window init:
manager = new QNetworkAccessManager();
QObject::connect
(manager, &QNetworkAccessManager::finished, this, [=](QNetworkReply *reply)
{
if (reply->error())
{
ui->debugText->appendHtml(reply->errorString());
return;
}
QString answer = reply->readAll();
ui->debugText->appendHtml(answer);
}
);
To which I get the reply:
Error transferring https://test-api.service.hmrc.gov.uk/hello/world -
server replied: Not Acceptable
I assume that means I am communicating with the sever now, but I do not know what this terse error message means!
The Java on the HMRC web page is as follows:
// construct the GET request for our Hello World endpoint
HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet(
"https://test-api.service.hmrc.gov.uk/hello/world");
request.addHeader("Accept", "application/vnd.hmrc.1.0+json");
// execute the request
HttpResponse response = client.execute(request);
// extract the HTTP status code and response body
int statusCode = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity());
Is that enough information for someone to point me in the right direction of what I'm doing wrong please? Suspect I am missing a fundamental point here.

In your Java example, you are setting the HTTP header "Accept". In your C++/Qt snippet, your are setting the "Content-Type" header.
You may want to adapt your code like this to match your Java working example:
QNetworkRequest request { QUrl(rs) };
request.setRawHeader(QByteArray("Accept"), QByteArray("application/vnd.hmrc.1.0+json"));
manager->get(request);

Related

HttpClient reads incomplete or malformed string

Im trying to figure out which part of my app (Xamarin Forms and proxy written in PHP) is buggy. Firstly I thought that my proxy (written in PHP) is working incorrectly with long set of data (ie. json containing 1.300.000 characters) and returns malformed response, but every single request with Postman gives me correct JSON, which is successfully decoded with third-party tools. So I assume, proxy is working well.
The problem is (I guess) with decoding response in my Xamarin Forms (2.0.0-beta.22) app. I'm using HttpClient to read response with this code:
response.EnsureSuccessStatusCode();
var entries = new List<HistoryEntry>();
var content = await response.Content.ReadAsStringAsync();
_loggerService.Error(content);
response is just GetAsync response from HttpClient. The problem is: content is randomly incomplete/malformed. Saying this I mean last character is missing (}) or JSON keys/values have additional " character, which breaks everything. Unfortunately, I can make exactly the same requests many times and once it works, once not. I found out that this behavior happens only with large set of data (as I mentioned before, long JSON string).
Is there any possibility that ReadAsStringAsync does not wait for full response or in any way alters my response string? How can I find the reason of wrongly downloaded data?
EDIT 21.05.2019:
Just copied valid JSON (available here: https://github.com/jabools/xamarin/blob/master/json.txt) and returned it from Lumen app by response()->json(json_decode(..., true)) and still the same result. Hope someone will be able to reproduce this and help me with this issue :( More informations in comments.
Used this code in C#:
Device.BeginInvokeOnMainThread(async () =>
{
for (int i = 0; i < 10; i++)
{
var response = await client.GetAsync("<URL_TO_PHP>");
//var response = await client.GetAsync("https://jsonplaceholder.typicode.com/photos");
var content = await response.Content.ReadAsAsync<object>();
Debug.WriteLine("Deserialized: " + i);
}
});

Make get request to web service, get json response and update GUI in Qt

Trying to learn Web Services with Qt (using Qt Creator 4.1.0) and connecting data to the GUI. I have read several online examples (most notably: 1, 2 and 3) but my low coding level along with the fact that I could not find full examples that were demonstrating my needs lead me here :).
I have created a simple example so that contains all my shortcomings:
make an HTTP get request to a (existing) web service every 30 seconds.
The web service then responds by sending a json data object (see below for such a json example format) which we receive and parse.
Then, the Qt will display all the parsed json data onto a simple GUI (see below for how such a GUI looks like.
json data format - example:
{
"city": "London",
"time": "16:42",
"unit_data":
[
{
"unit_data_id": "ABC123",
"unit_data_number": "21"
}
]
}
My simple Qt GUI design (made in Qt Creator) displaying all the fetched parsed data:
I would really appreciate any full code example that show how we can make a request to a web service and then how to fetch the json response. Finally, how to connect the GUI in Qt to display this data as soon as they are received.
I am just starting studying this area and I am in need of a simple full code example to get me going.
Here is a fully working example on how to send a GET request with parameters to a web service using QNetworkAccessManager and parse the JSON response using QJsonDocument.
In the example, I am sending a request to http://uinames.com/ whose responses are encoded in JSON in the following format:
{
"name":"John",
"surname":"Doe",
"gender":"male",
"region":"United States"
}
I am parsing the JSON response and displaying it in a GUI.
#include <QtWidgets>
#include <QtNetwork>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//setup GUI (you could be doing this in the designer)
QWidget widget;
QFormLayout layout(&widget);
QLineEdit lineEditName;
QLineEdit lineEditGender;
QLineEdit lineEditRegion;
auto edits = {&lineEditName, &lineEditGender, &lineEditRegion};
for(auto edit : edits) edit->setReadOnly(true);
layout.addRow("Name:", &lineEditName);
layout.addRow("Gender:", &lineEditGender);
layout.addRow("Region:", &lineEditRegion);
QPushButton button("Get Name");
layout.addRow(&button);
//send request to uinames API
QNetworkAccessManager networkManager;
QObject::connect(&networkManager, &QNetworkAccessManager::finished,
[&](QNetworkReply* reply){
//this lambda is called when the reply is received
//it can be a slot in your GUI window class
//check for errors
if(reply->error() != QNetworkReply::NoError){
for(auto edit : edits) edit->setText("Error");
networkManager.clearAccessCache();
} else {
//parse the reply JSON and display result in the UI
QJsonObject jsonObject= QJsonDocument::fromJson(reply->readAll()).object();
QString fullName= jsonObject["name"].toString();
fullName.append(" ");
fullName.append(jsonObject["surname"].toString());
lineEditName.setText(fullName);
lineEditGender.setText(jsonObject["gender"].toString());
lineEditRegion.setText(jsonObject["region"].toString());
}
button.setEnabled(true);
reply->deleteLater();
});
//url parameters
QUrlQuery query;
query.addQueryItem("amount", "1");
query.addQueryItem("region", "United States");
QUrl url("http://uinames.com/api/");
url.setQuery(query);
QNetworkRequest networkRequest(url);
//send GET request when the button is clicked
QObject::connect(&button, &QPushButton::clicked, [&](){
networkManager.get(networkRequest);
button.setEnabled(false);
for(auto edit : edits) edit->setText("Loading. . .");
});
widget.show();
return a.exec();
}
Edit:
Since you asked about how to use a QTimer to trigger the update every one minute, replace the connect call of the button clicked signal from the code above with something like this:
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&](){
networkManager.get(networkRequest);
button.setEnabled(false);
for(auto edit : edits) edit->setText("Loading. . .");
});
timer.start(60000); //60000 msecs = 60 secs
As noted in comments, if you are using this in your window class's constructor, you have to make sure that the networkManager, networkRequest, the GUI components, and the timer here are kept alive as long as your window object is running. So, you may choose to allocate them in the heap or as class members.

Json put/get/post in Restful webservices not working

I am trying to pass parameters to a server and extract the report in csv format. So the code i have has PUT/GET/POST in the order. I could get GET and POST work, but when i add PUT there is no error just blank screen.
String output1 = null;
URL url = new URL("http://<servername>/biprws/raylight/v1/documents/12345/parameters");
HttpURLConnection conn1 = (HttpURLConnection) url.openConnection();
conn1.setRequestMethod("PUT");
conn1.setRequestProperty("Accept", "application/json");
conn1.setRequestProperty("Content-Type", "application/json; charset=utf-8");
conn1.setDoInput(true);
conn1.setDoOutput(true);
String body = "<parameters><parameter><id>0</id><answer><values><value>EN</value></values></answer></parameter></parameters>";
int len1 = body.length();
conn1.setRequestProperty("Content-Length", Integer.toString(len1));
conn1.connect();
OutputStreamWriter out1 = new OutputStreamWriter(conn1.getOutputStream());
out1.write(body, 0, len1);
out1.flush();
What i am trying to do is pass parameter EN to the report and refresh it, take the output in csv using GET. POST is used for login to the server. I could make GET and POST work and get the output in CSV but not refreshed one.
Appreciate very much any help here.
Thanks,
Ak
What is the response code from the server when using PUT?
A PUT may not actually return a body to display on the screen; often times a PUT will only return a 200 or 204 response code. 204 would clearly mean that the server took the data and applied it, but is not sending you anything back, 200/201 may include a response, but maybe not. It depends on the folks who implemented the API.
https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html (section 9.6)
Should a RESTful 'PUT' operation return something

PUT requests with Custom Ember-Data REST Adapter

I'm using Ember-Data 1.0.0.Beta-9 and Ember 1.7 to consume a REST API via DreamFactory's REST Platform. (http://www.dreamfactory.com).
I've had to extend the RESTAdapter in order to use DF and I've been able to implement GET and POST requests with no problems. I am now trying to implement model.save() (PUT) requests and am having a serious hiccup.
Calling model.save() sends the PUT request with the correct data to my API endpoint and I get a 200 OK response with a JSON response of { "id": "1" } which is what is supposed to happen. However when I try to access the updated record all of the properties are empty except for ID and the record on the server is not updated. I can take the same JSON string passed in the request, paste it into the DreamFactory Swagger API Docs and it works no problem - response is good and the record is updated on the DB.
I've created a JSBin to show all of the code at http://emberjs.jsbin.com/nagoga/1/edit
Unfortunately I can't have a live example as the servers in question are locked down to only accept requests from our company's public IP range.
DreamFactory provides a live demo of the API in question at
https://dsp-sandman1.cloud.dreamfactory.com/swagger/#!/db/replaceRecordsByIds
OK in the end I discovered that you can customize the DreamFactory response by adding a ?fields=* param to the end of the PUT request. I monkey-patched that into my updateRecord method using the following:
updateRecord: function(store, type, record) {
var data = {};
var serializer = store.serializerFor(type.typeKey);
serializer.serializeIntoHash(data, type, record);
var adapter = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
// hack to make DSP send back the full object
adapter.ajax(adapter.buildURL(type.typeKey) + '?fields=*', "PUT", { data: data }).then(function(json){
// if the request is a success we'll return the same data we passed in
resolve(json);
}, function(reason){
reject(reason.responseJSON);
});
});
}
And poof we haz updates!
DreamFactory has support for tacking several params onto the end of the requests to fully customize the response - at some point I will look to implement this correctly but for the time being I can move forward with my project. Yay!
EmberData is interpreting the response from the server as an empty object with an id of "1" an no other properties in it. You need to return the entire new object back from the server with the changes reflected.

Submitting an HTML form in Android without WebView

I was given an assignment to develop a very simple weather app in Android using the data given from
http://forecast.weather.gov/zipcity.php
This would be my first actual android app, my only other experience is watching/reading many tutorials on getting started.
To give some background information of the app, it simply needs to have one activity that is a
text field for the user to enter a city, state combination, or just a zip code. This then needs to be submitted to the website's (listed above) form.
After this has been submitted, my app needs to retrieve page sent in response and display it in a simple layout (possibly a listView of the days of the week).
My first question is, how would I go about submitting a text input to the above website's form? Would I have to know the name of the form's field to submit to it precisely?
In general, how should I start this process?
Edited version:
Using this code:
HttpClient httpClient = new DefaultHttpClient();
HttpPost post = new HttpPost("http://forecast.weather.gov/zipcity.php?inputstring=04419");
HttpResponse httpResp;
try {
httpResp = httpClient.execute(post);
StatusLine status = httpResp.getStatusLine();
Toast.makeText(getApplicationContext(), status.getStatusCode(), Toast.LENGTH_SHORT).show();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
I'm getting a:
10-26 15:29:56.686: DEBUG/SntpClient(59): request time failed: java.net.SocketException: Address family not supported by protocol
error in the debug view. Is this error related? Is my use of toast reasonable for testing if this http interaction is successful?
When you want to see what a post request in passing use Firefox + tamperdata, that way you can look at how to use the webservice.
I took a look at it, I searched for "33129" and when you enter text on the left hand box to start a search it simply pases 2 parameters:
inputstring = "33129"
Go2 = "Go"
That would be one way to do it, on the other hand, once the request is finished it transforms it into another request, thats more specific. You can search by city state or zip, not both.
If you request with a zip you get redirected to:
http://forecast.weather.gov/MapClick.php?CityName=Miami&state=FL&site=MFL&lat=25.77&lon=-80.2
So there is probably a redirection going on there.
You are going to have to take a close look at how to work with this.
Now, to do a post request in android use a name value pair like this.
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("inputstring","33129));
Ramp's answer shows the rest.
Also, ALL comunication should be done on a thread that is not the main UI thread or you will get a ANR error.
When I checked out your URL, I entered city and state.This is the URL it generates in HTTP POST :
http://forecast.weather.gov/MapClick.php?CityName=Edison&state=NJ&site=PHI&textField1=40.5288&textField2=-74.3693
So your will do a HTTP POST with the CityName and state URL parameters. It will be something like this if you use apache HttpClient in your android program :
( It should be OK to use HTTP GET too I guess)
HttpPost post = new HttpPost(url);//construct url with cityName and State
HttpResponse httpResp = httpClient.execute(post);
StatusLine status = httpResp.getStatusLine();
Hope this helps.
Something like this:
HttpClient hc = new DefaultHttpClient();
List<NameValuePair> l = new ArrayList<NameValuePair>(6);
l.add(new BasicNameValuePair("fieldname", "fieldvalue"));
//More fields...
HttpPost pr = new HttpPost(TheURL);
pr.setEntity(new UrlEncodedFormEntity(l, "the-proper-encoding"));
HttpResponse Resp = hc.execute(pr);
if(Resp.getStatusLine().getStatusCode() != 200)
//Fail...