PDA

View Full Version : Reqest and Response in XML



srohit24
26th June 2009, 10:26
Hi

I am working on a project that uses Basic authentication scheme (http://en.wikipedia.org/wiki/Basic_authentication_scheme) to authenticate a user.

I need to call a api in a website.I need to send the username and password in base64 format.

After the header has the host and authentication details, I need to send the request in this format.

=== Request ===

<request client_id="1" client_serial_number="abcded" version="0.1">
<call name="test.reverse">abc</call>
</request>

This is the responce that i should get if all goes well

=== Response ===

<response version="0.1">
<value>cba</value>
</response>

For the header request part, i am using the following code


QXmlStreamReader xml;
QHttp http;

void senddata::fetch()
{

QString username = "abc";
QString password = "12345";

xml.clear();

QUrl url(lineEdit->text());

http.setHost(url.host());
http.setUser(username,password);
connectionId = http.get(url.path());
}
I am reading the data here


void senddata::readData(const QHttpResponseHeader &resp)
{
if (resp.statusCode() != 200)
http.abort();
else {
xml.addData(http.readAll());
parseXml();// This funtion has nothing in it as of now
}
}

Is my code correct? How should i proceed?

How can i parse the response xml and get the data i need?

srohit24
30th June 2009, 13:05
can somebody help me with this??

wysota
30th June 2009, 13:08
For such simple things I would use QDomDocument instead of the stream reader.

srohit24
30th June 2009, 13:35
thanks for replying mate.

I think what u suggested is what i need for proper functioning

abt headers and data, how can I send the data along with the header? I am stuck here. :(

Can QDomDocument also manage the response as well as the QHttpResponseHeader?

wysota
30th June 2009, 13:57
QDomDocument is for parsing xml data, it has nothing to do with HTTP.

srohit24
1st July 2009, 02:46
thanks mate.

can somebody guide me as how i can send the data along with my header??

wysota
1st July 2009, 08:53
Did you notice QHttp::post() and QHttp::request()?

srohit24
1st July 2009, 15:34
ok.

int QHttp::request ( const QHttpRequestHeader & header, QIODevice * data = 0, QIODevice * to = 0 )

can you explain the function in a better way, than the explanation that is present in Qt Documentation?

what should i assign in the QHttpRequestHeader and in QIODevice?

this is the request header


QByteArray string ("username");
string.append(":");
string.append("12345");
QString converted = string.toBase64();
//qDebug() <<"String"<< string<<"converted" <<converted ;

QUrl url(lineEdit->text());

http.setHost(url.host());

connectionId = http.get(url.path());

this is the data that has to be sent along with the header.


void MainWindow::sendrequest()
{
QDomDocument doc("request");
QDomElement root = doc.createElement("request");
doc.appendChild(root);

QDomElement tag = doc.createElement("call");
root.appendChild(tag);

QDomText t = doc.createTextNode("abc");
tag.appendChild(t);

QString xml = doc.toString();
}

can u help me as how I should call the request function??

Will using QNetworkAccessManager for the request and response from a webpage, be easier?

thanks

wysota
1st July 2009, 15:44
"data" is a pointer to a QIODevice (such as QBuffer) containing the data the engine should send to the server as the request (i.e. the result of your sendrequest() method). As for the header - the least you have to provide is the method (GET or POST) and relative path of the URL that should be queried.

srohit24
1st July 2009, 16:12
thanks mate.

so i need to call the request, like this?
http.request( GET, xmlrequest);

I need to have the xml request like this

<request client_id="1" client_serial_number="abcded" version="0.1">
<call name="test.reverse">abc</call>
</request>

but after using this code,



QDomDocument doc("request");
QDomElement root = doc.createElement("request");
doc.appendChild(root);

QDomElement tag = doc.createElement("call");
QString name = "teci.test.reverse";
QString value = "abc";
tag.setAttribute(name,value);
root.appendChild(tag);

QString xmlrequest = doc.toString();


i am able to only this.

"<!DOCTYPE request>
<request>
<call test.reverse="abc" />
</request>

can you help me with this?

wysota
1st July 2009, 16:44
thanks mate.

so i need to call the request, like this?
http.request( GET, xmlrequest);

Not really, you have to match the argument types (and values of course).


I need to have the xml request like this

<request client_id="1" client_serial_number="abcded" version="0.1">
<call name="test.reverse">abc</call>
</request>

but after using this code,



QDomDocument doc("request");
QDomElement root = doc.createElement("request");
doc.appendChild(root);

QDomElement tag = doc.createElement("call");
QString name = "teci.test.reverse";
QString value = "abc";
tag.setAttribute(name,value);
root.appendChild(tag);

QString xmlrequest = doc.toString();


i am able to only this.

"<!DOCTYPE request>
<request>
<call test.reverse="abc" />
</request>

What more would you expect from the above code snippet?

srohit24
2nd July 2009, 05:17
i want to exact copy of the request xml thats been given.

I am asking, what changes should i make to get the xml that will request the server.

srohit24
2nd July 2009, 07:33
i have made some changes to my code

I first need to get the Client id and the serial no



//This is how i need to send the request
=== Request ===

<request version="0.1">
<call name="client.create">
<client>
<kind>MOBILE</kind>
<name>API Test suite 7936</name>
</client>
</call>
</request>

//This is what i will get, if everything goes well

=== Response ===

<response version="0.1">
<client id="1">
<name>API Test suite 7936</name>
<kind>MOBILE</kind>
<serial_number>a09b4d3b-08b7-4cdf-bf3c-a5a8fb27ee1f</serial_number>
<added_on>2009-06-25 12:33:49.182287</added_on>
<enabled>True</enabled>
</client>
</response>



My code to send the request is this


void MainWindow::fetch()
{

QByteArray string ("username");
string.append(":");
string.append("12345");
QString converted = string.toBase64();

xml.clear();

QUrl url("http://something/");

http.setHost(url.host(),url.port(80));
http.setUser(converted);
connectionId = http.get(url.path());

sendrequest();
http.post(url.path(),xmlrequest);

}

void MainWindow::sendrequest()
{
QDomDocument doc;
QDomElement root = doc.createElement("request");
root.setAttribute("version", "0.1");
doc.appendChild(root);

QDomElement tag = doc.createElement("call");
tag.setAttribute("name","teci.client.create");
root.appendChild(tag);

QDomElement tag1 = doc.createElement("client");
tag.appendChild(tag1);

QDomElement tag2 = doc.createElement("kind");
QDomText t = doc.createTextNode("MOBILE");
tag2.appendChild(t);
tag1.appendChild(tag2);

QDomElement tag3 = doc.createElement("name");
t = doc.createTextNode("API Test suite 7936");
tag3.appendChild(t);
tag1.appendChild(tag3);

xmlrequest.append(doc.toString());
qDebug() << xmlrequest.data();
}

output i am getting is this



<request version="0.1" >
<call name="client.create" >
<client>
<kind>MOBILE</kind>
<name>API Test suite 7936</name>
</client>
</call>
</request>

Received error during HTTP fetch.

What should I do to get the program working?

The request is according to the specification. What am i missing here?

wysota
2nd July 2009, 08:56
Why are you sending both get and post? And how do you process the responses?

srohit24
2nd July 2009, 10:03
i have a function to read the xml and then to parse it.

but as i am getting a error during fetch, i didnt show the code.

abt both get and post, i am not sure which one I should be using. so i was trying both of them

wysota
2nd July 2009, 10:16
You should use POST if you want to send some data to the server.

srohit24
2nd July 2009, 10:19
ok. i have made the change ( using http.post now)

but still the same problem

Received error during HTTP fetch.

I have the following code to read the data and to check if the server has finished sending


void RSSListing::readData(const QHttpResponseHeader &resp)
{
qDebug() << "XML"<<http.readAll();
if (resp.statusCode() != 200)
http.abort();
else {
xml.addData(http.readAll());
parseXml();
}
}


void RSSListing::finished(int id, bool error)
{
if (error) {
qWarning("Received error during HTTP fetch.");
lineEdit->setReadOnly(false);
abortButton->setEnabled(false);
fetchButton->setEnabled(true);
}
else if (id == connectionId) {
lineEdit->setReadOnly(false);
abortButton->setEnabled(false);
fetchButton->setEnabled(true);
}
}

wysota
2nd July 2009, 11:03
And when exactly does the error occur?

srohit24
2nd July 2009, 11:28
I have a UI, which has 2 buttons and a text area

When i click on the 1st button, the fetch function is called.
The 2nd button is abort

When i click on the fetch button, the http sends the data.

But i am not able to receive anything from the server

wysota
2nd July 2009, 12:05
Try to get more details about the request/response that triggers the error. What does QHttp::errorString() return?

srohit24
2nd July 2009, 14:54
I getting "Unknown error" as the output for the error string, when i use it in the fetch function

But when i use it in the finished function, am getting "Authentication required".

But i am passing the username and password in the variable converted under http.setUser.

any ideas as to what might be the problem??

wysota
2nd July 2009, 15:03
Use a network sniffer to see how your request looks like when it leaves your network interface. See if the proper auth data is contained in the http request.

srohit24
2nd July 2009, 15:54
i used the network sniffer

here is what i got, in packets



HOST

.à ügÇT.¡„Ï3ˆd.>/.!E.-ú@.€JT;\*I®…R\.P²4Þ *·}ÓPÿÿ)¥..POST /api/ HTTP/1.1
Connection: Keep-Alive
Host: ionlab.webfactional.com
content-length: 162

<request version="0.1" >
<call name="teci.client.create" >
<client>
<kind>MOBILE</kind>
<name>API Test suite 7936</name>
</client>
</call>
</request>


SERVER

.¡„Ï3.à ügÇTˆd.>D.!E.B“i@.6Ю…R;\*I.P\* }Ó²4ßÁP Z..HTTP/1.1 401 Authorization Required
Server: nginx
Date: Thu, 02 Jul 2009 14:41:37 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Cookie
WWW-Authenticate: Basic realm="TrackEveryCoin API"

16
Authorization Required
0

HOST

.à ügÇT.¡„Ï3ˆd.>Å .!E.ˆÿ@.€Iô;\*I®… R\.P²4ßÁ*·~Ã*Pþå¾I..POST /api/ HTTP/1.1
Connection: Keep-Alive
Host: ionlab.webfactional.com
content-length: 162
Authorization: Basic QXV0aG9yaXphdGlvbiA6IGMzSnZhR2wwTWpSQVoyMWhhV3d1WT I5dE9qRXlNelExOg==

<request version="0.1" >
<call name="teci.client.create" >
<client>
<kind>MOBILE</kind>
<name>API Test suite 7936</name>
</client>
</call>
</request>


SERVER

.¡„Ï3.à ügÇTˆd.>D.!E.B“j@.6Ï®…R;\*I.P\* ~Ã*²4á!PPSk..HTTP/1.1 401 Authorization Required
Server: nginx
Date: Thu, 02 Jul 2009 14:41:37 GMT
Content-Type: text/html; charset=ut-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Cookie
WWW-Authenticate: Basic realm="TrackEveryCoin API"

16
Authorization Required
0


Before the complete data is being sent, i am getting the authentication required error

wysota
2nd July 2009, 17:04
You are not sending the authentication data. Make sure you use setUser() early enough in the flow. Also consider switching from QHttp to QNetworkAccessManager, it's easier to use it.

srohit24
3rd July 2009, 09:45
i got it working mate. thanks for ur help

here is the code that works


void RSSListing::fetch()
{
lineEdit->setReadOnly(true);
fetchButton->setEnabled(false);
abortButton->setEnabled(true);
treeWidget->clear();
xml.clear();

QByteArray string ("username");
string.append(":");
string.append("12345");
QString encoded = string.toBase64();
qDebug() <<"String"<< string <<"Encoded" <<encoded ;

QUrl url("something/api/");
url.setUserName("username");
url.setPassword("12345");

QHttpRequestHeader header("POST","/api/");
header.setValue("Host",url.host());
header.setValue("Path",url.path());
header.setValue("Authorization","Basic " + encoded);

http.setHost(url.host(),url.port(80));

sendrequest();
connectionId = http.request(header,xmlrequest);
}

I am getting the response as per the specifications.

While parsing, is it possible to go to a particular tag, say skip the 1st 3 tags and go directly to the 4th one?

The first few tags in the response is redundant, i dont want to waste time parsing through them.

thanks

srohit24
3rd July 2009, 12:45
My parsing code is this.


void MainWindow::parseXml()
{

while (!xml.atEnd())
{
xml.readNext();

if (xml.isStartElement())
{
currentTag = xml.name().toString();
qDebug() << "Current tag"<< currentTag ;

}
else if (xml.isEndElement())
{
if (xml.name() == "response")
{

QTreeWidgetItem *item = new QTreeWidgetItem;
item->setText(0, titleString);
item->setText(1, linkString);
treeWidget->addTopLevelItem(item);

titleString.clear();
linkString.clear();
}

}
else if (xml.isCharacters() && !xml.isWhitespace())
{
if (currentTag == "client")
titleString += xml.attributes().value("id").toString();
else if (currentTag == "serial_number")
linkString += xml.text().toString();
}
}
if (xml.error() && xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) {
qWarning() << "XML ERROR:" << xml.lineNumber() << ": " << xml.errorString();
http.abort();
}
}



<response version="0.1">
<client id="1">
<name>API Test suite 7936</name>
<kind>MOBILE</kind>
<serial_number>a09b4d3b-08b7-4cdf-bf3c-a5a8fb27ee1f</serial_number>
<added_on>2009-06-25 12:33:49.182287</added_on>
<enabled>True</enabled>
</client>
</response>

I am able to get the serial no from the response, but to get the value of id in client tag is proving hard.

how can i get the value from a attribute and convert it into string??

wysota
3rd July 2009, 13:24
See QXmlStreamReader::attributes() and QXmlStreamAttributes::value().

srohit24
4th July 2009, 05:04
thanks mate.

I was trying to find the tag when it was already parsed. Fixed it. working now.

Can you tell me a method through which i can go directly to a particular tag in the QXMLStreamHeader??

I want to skip the 1st 3 tags and go directly to the 4th one.

wysota
4th July 2009, 07:57
Consider using QDomDocument instead of the stream reader. It is easier to manipulate the xml there.