Another XMLHttpRequest question

8 views
Skip to first unread message

Uwe Feldtmann

unread,
Jan 4, 2007, 7:06:18 AM1/4/07
to pylons-...@googlegroups.com
In the form handling document it states that if POST is used the data is
available in request.params just as for a GET operation the only
difference being that the data is sent in the body and not in the url.

I'm sending POST data via XMLHttpRequest as follows:-

xhr = new XMLHttpRequest()
... callback omitted
xhr.open( "POST", url, true ) - where url is /contr/func
xhr.send( data ) - where data is "?parm1=value1&parm2=value2"

When I try to retrieve the values in the controller function using:-

usr = request.params['parm1']

I get this error:-
Error - exceptions.KeyError: "'parm1'"

Do I need to change the formatting of the data I send?

s = request.body.read(int(request.environ['CONTENT_LENGTH']))
print s

gives me this

?parm1=value1&parm2=value2

Can someone shed some light here please?

I am using 0.9.4 and calling from Firefox

Uwe Feldtmann

unread,
Jan 4, 2007, 6:50:56 PM1/4/07
to pylons-discuss
I sent this by e-mail 24 hours ago and it hasn't appeared - not sure
why. Sorry if it gets here twice.

In the form handling document it states that if POST is used the data
is available in request.params just as for a GET operation the only
difference being that the data is sent in the body and not in the url.

I'm sending POST data via XMLHttpRequest as follows:-

xhr = new XMLHttpRequest()
... callback omitted xhr.open( "POST", url, true ) - where url is
/contr/func

xhr.send( data ) - where data is a string

Robert Leftwich

unread,
Jan 4, 2007, 7:16:37 PM1/4/07
to pylons-...@googlegroups.com
Uwe Feldtmann wrote:
>
> I'm sending POST data via XMLHttpRequest as follows:-
>
> xhr = new XMLHttpRequest()
> ... callback omitted xhr.open( "POST", url, true ) - where url is
> /contr/func
> xhr.send( data ) - where data is a string
> "?parm1=value1&parm2=value2"
>
> When I try to retrieve the values in the controller function using:-
>
> usr = request.params['parm1']
>
> I get this error:-
> Error - exceptions.KeyError: "'parm1'"
>
> Do I need to change the formatting of the data I send?

Try removing the ? and keeping the &, i.e.

data = "parm1=value1&parm2=value2";
xhr.send( data );

HTH

Robert

PS You might also benefit from using a higher level js library, less things to
code/worry about yourself. I'm a big fan of YUI from Yahoo -
http://developer.yahoo.com/yui/ - great documentation, used on some of the
highest loaded/profile sites on the net, great support.

Philip Jenvey

unread,
Jan 4, 2007, 7:20:47 PM1/4/07
to pylons-...@googlegroups.com

On Jan 4, 2007, at 3:50 PM, Uwe Feldtmann wrote:

>
> I sent this by e-mail 24 hours ago and it hasn't appeared - not sure
> why. Sorry if it gets here twice.
>

Your first one just arrived before this one, for some reason. =]

> In the form handling document it states that if POST is used the data
> is available in request.params just as for a GET operation the only
> difference being that the data is sent in the body and not in the url.
>
> I'm sending POST data via XMLHttpRequest as follows:-
>
> xhr = new XMLHttpRequest()
> ... callback omitted xhr.open( "POST", url, true ) - where url is
> /contr/func
> xhr.send( data ) - where data is a string
> "?parm1=value1&parm2=value2"

Just this data would be an invalid POST request. You need a Content-
Type header set, and I'd recommend having a valid Content-Length
header as well. Google should have plenty of XMLHTTPRequest POST
examples.

Also, consider adding those params, unencoded, onto the end of your
URL, and making a GET request (access them via request.GET). This
would be easier if you're not sending lots of data.

--
Philip Jenvey

Uwe Feldtmann

unread,
Jan 4, 2007, 8:45:11 PM1/4/07
to pylons-...@googlegroups.com
Robert Leftwich wrote:
> Try removing the ? and keeping the &, i.e.
>
> data = "parm1=value1&parm2=value2";
> xhr.send( data );
>
> HTH
>
> Robert
>
Tried that and it makes no difference. I still get the same error.

The problem appears to be somewhere in Pylons I think. It doesn't seem
to be decoding the parms correctly from an XMLHttpRequest when the data
is sent vis .send() which is what I need. If I append the data to the
url it works fine, however, I may need to send more than the 512byte
limit and I also don't want the data on the url for security reasons.

Any other suggestions? Where should I look?


> PS You might also benefit from using a higher level js library, less
> things to code/worry about yourself. I'm a big fan of YUI from Yahoo -
> http://developer.yahoo.com/yui/ - great documentation, used on some of
> the highest loaded/profile sites on the net, great support.

I've been thinking about using one of these libraries but I want to keep
the js lean and on purpose. I reserve the right to change my mind if it
proves too difficult. ;-)

Uwe Feldtmann

unread,
Jan 4, 2007, 8:49:57 PM1/4/07
to pylons-...@googlegroups.com
Philip Jenvey wrote:
> Your first one just arrived before this one, for some reason. =]
Who knows. The message showed up as soon as I posted the entry via the
groups page. Hmmmm.

> Just this data would be an invalid POST request. You need a Content-
> Type header set, and I'd recommend having a valid Content-Length
> header as well. Google should have plenty of XMLHTTPRequest POST
> examples.
The Content-Length is correctly set or so it seams and I've looked at so
many examples my head is swimming.
This is the header info as reported by Pylons.
CONTENT_LENGTH: '20'
CONTENT_TYPE: 'application/xml'

> Also, consider adding those params, unencoded, onto the end of your
> URL, and making a GET request (access them via request.GET). This
> would be easier if you're not sending lots of data.
I need to use POST and have data that may exceed the 512byte limit of
the URL.

Thanks Philip - any other thoughts?

Philip Jenvey

unread,
Jan 4, 2007, 9:34:09 PM1/4/07
to pylons-...@googlegroups.com

I missed this before, but you want to omit the leading '?'. What
you've been POSTing will result in 'value1' being set for the param
named '?param1'.

Also, you don't want to encode the parameters when POSTing either.

--
Philip Jenvey

Robert Leftwich

unread,
Jan 4, 2007, 9:38:22 PM1/4/07
to pylons-...@googlegroups.com
Uwe Feldtmann wrote:
>
> The Content-Length is correctly set or so it seams and I've looked at so
> many examples my head is swimming.
> This is the header info as reported by Pylons.
> CONTENT_LENGTH: '20'
> CONTENT_TYPE: 'application/xml'

Set the content type to "application/x-www-form-urlencoded" to get the data
available in request.params. As "application/xml", Pylons/Paste won't try and
decode the incoming data.

Robert

Uwe Feldtmann

unread,
Jan 5, 2007, 12:09:06 AM1/5/07
to pylons-...@googlegroups.com
Thank you Robert and Philip for all your help.

One more gotcha for anyone trying this at home is that the content-type
can only be set after the .open() call in the XMLHttpRequest.

The working request code:-

function xhrpost( url, data, callback ) {
xhr = new XMLHttpRequest();
xhr.open("POST", url, true);

// note setRequestHeader must appear after .open

xhr.setRequestHeader( "Content-Type",
"application/x-www-form-urlencoded" );

if ( callback ) {
xhr.onreadystatechage = function() {
if ( xhr.readyState==4 ) {
callback( xhr.responseText );
}
}
}
if ( data ) {
xhr.send( data );
} else {
xhr.send( null );
}
};

Data is passed in as a string "parm1=val&parm2=val2"

Now to get the callback working.

I'm sending back a simple text status at the end of the controller
method with:-

resp = Response()
resp.write('ok')
return resp

But this has now stopped working since changing the request type from
GET to POST.

A call via GET works but since I changed the request to POST it's not
sending anything back to javascript even though I've registered a callback.

Any clues?

Robert Leftwich

unread,
Jan 5, 2007, 1:06:25 AM1/5/07
to pylons-...@googlegroups.com
Uwe Feldtmann wrote:
>
> A call via GET works but since I changed the request to POST it's not
> sending anything back to javascript even though I've registered a callback.
>
> Any clues?
>

In these sorts of situations the LiveHTTPHeaders extension for FF is a big help.
If you run it and look at the captured information after issuing the POST you
might find a hint, i.e. an error status is being returned or similar, some
condition that the js is not handling.

> xhr.onreadystatechage = function() {
^^
Also, is that an email typo or is it incorrect in the real code?

What happens if you use:

if ( xhr.readyState==4 ) {
alert(xhr.status) and/or alert(xhr.responseText);
...

HTH

Robert

Uwe Feldtmann

unread,
Jan 5, 2007, 1:44:51 AM1/5/07
to pylons-...@googlegroups.com
I've been using the Live headers and the result is:-

The original request....

POST /xul/c_user HTTP/1.1
Host: localhost:5000
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.0.9)
Gecko/20070102 Ubuntu/dapper-security Firefox/1.5.0.9
Accept:
text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5

Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
Pragma: no-cache
Cache-Control: no-cache
uname=asdasd&pword=asdasd

The response...

HTTP/1.x 200 OK
Server: PasteWSGIServer/0.5 Python/2.4.3
Date: Fri, 05 Jan 2007 06:39:12 GMT
Content-Type: text/html; charset=UTF-8
Connection: close

There is no content.

Robert Leftwich wrote:
> In these sorts of situations the LiveHTTPHeaders extension for FF is a
> big help. If you run it and look at the captured information after
> issuing the POST you might find a hint, i.e. an error status is being
> returned or similar, some condition that the js is not handling.
>
>> xhr.onreadystatechage = function() {
> ^^
> Also, is that an email typo or is it incorrect in the real code?

At the moment the code reads

xhr.onreadystatechage = function() {
if ( xhr.readyState==4 ) {

alert( xhr.responseText );
}
};

Which would normally read

xhr.onreadystatechage = function() {
if ( xhr.readyState==4 ) {

func( xhr.responseText );
}
};

where func is the passed in callback function.


Robert Leftwich

unread,
Jan 5, 2007, 3:23:00 AM1/5/07
to pylons-...@googlegroups.com
Uwe Feldtmann wrote:
>
> I've been using the Live headers and the result is:-
>
> The original request....
>
> POST /xul/c_user HTTP/1.1
>
> The response...
>
> HTTP/1.x 200 OK
> Server: PasteWSGIServer/0.5 Python/2.4.3
> Date: Fri, 05 Jan 2007 06:39:12 GMT
> Content-Type: text/html; charset=UTF-8
> Connection: close
>
> There is no content.

And you are sure that the correct controller is being called? Maybe the mappings
aren't setup quite right?

What happens if you use Python to call the controller using POST? e.g.

import urllib2
u = urllib2.urlopen("http://localhost:5000/xul/c_user", "uname=asdasd&pword=asdasd")
u.read()


FWIW I added the following to a controller called 'home',

def test(self):
print request.params['uname'], request.params['pword']


resp = Response()
resp.write('ok')
return resp

called it with:

u = urllib2.urlopen("http://localhost:8000/home/test",
"uname=asdasd&pword=asdasd")
u.read()

and it printed

'ok'

as expected.

>
> At the moment the code reads
>
> xhr.onreadystatechage = function() {

...snip


>
> Which would normally read
>
> xhr.onreadystatechage = function() {

...snip

That should be *onreadystatechange* not onreadystatechage, i.e. there is a
missing n in change.

Robert

Uwe Feldtmann

unread,
Jan 5, 2007, 7:48:39 PM1/5/07
to pylons-...@googlegroups.com
Robert Leftwich wrote:

..snip
> That should be *onreadystatechange* not onreadystatechage, i.e. there
> is a missing n in change.

Isn't peer programming wonderful. Thanks Robert.
I've been looking at that code all day and just couldn't see the typo.
New glasses needed perhaps.

It's all working now thanks.

Markm

unread,
Jan 25, 2007, 2:50:45 PM1/25/07
to pylons-discuss
Hi Uwe
I am just begining here ... do you mind if I see that your complete (I
mean your controller and defs) code? For eg, I did not understand this
piece of code:

xhr.onreadystatechange = function()

What is this function() ??

I'm fighting with the same problem and it'll be very useful if I could
learn with you!

Thanks in advance.
Marcus


On 5 jan, 22:48, Uwe Feldtmann <u...@microshare.com.au> wrote:
> Robert Leftwich wrote:..snip> That should be *onreadystatechange* not onreadystatechage, i.e. there

> > is a missing n in change.Isn't peer programming wonderful. Thanks Robert.

Bob Ippolito

unread,
Jan 25, 2007, 2:57:27 PM1/25/07
to pylons-...@googlegroups.com
function () { ... } is the definition of an anonymous function. Like a
lambda in python.

Uwe Feldtmann

unread,
Jan 26, 2007, 12:45:35 AM1/26/07
to pylons-...@googlegroups.com
Markm wrote:
> I am just begining here ... do you mind if I see that your complete (I
> mean your controller and defs) code? For eg, I did not understand this
> piece of code:
>
> I'm fighting with the same problem and it'll be very useful if I could
> learn with you!

Hi Markus.

I see that Bob Ippolito has already explained the function() { ... }
definition - thanks Bob.

Most of the code I used was only to test out sending data back and forth
between the server and the browser without using html forms and would be
more confusing than it's worth so I'll sum up what I've learned from
experimentation and with the patient assistance of others on this list
(thank you all). If anything here is incorrect or could be done better
then please let us all know.

Simple rules I read somewhere:-
Use GET if the data you are sending to the server is less than 512 bytes
and can be sent on the url.
Use the POST method if the data is more than 512 bytes or needs to be
more secure.

---------------

Sending from the browser using GET:-

function toServerByGET(url, fct) {
xmlhttp = new XMLHttpRequest();

xmlhttp.onreadystatechange = function() {
if ( xmlhttp.readyState==4 ) {
fct( xmlhttp.responseText );
}
};

xmlhttp.open("GET",url);
xmlhttp.send(null);
}

and is called like this

urlAndData = "/controllerName/methodName/?parm1=" + value1 +
"&amp;parm2=" + value2;
toServer( urlAndData, responseHandler )

---------------

Sending from the browser using POST:-

function toServerByPOST( url, data, fct ) {
xmlhttp = new XMLHttpRequest();

xmlhttp.open( "POST", url, true );

// note setRequestHeader must appear after .open and is used
// to encode the data as though it came from a form (thanks Robert)
xmlhttp.setRequestHeader( "Content-Type",
"application/x-www-form-urlencoded" );

xmlhttp.onreadystatechange = function() {
if ( xmlhttp.readyState==4 ) {
fct( xmlhttp.responseText );
}
}

xmlhttp.send( data );
};

and is called like this

var data = {
id: 1,
parm1: "value1"
parm2: "value2"
}
toServerByPOST( "/controllerName/methodName", data, responseHandler );

---------------

The method in the controller is the same in both instances:-

class ControllerNameController(BaseController):

def methodName(self):
value1 = request.params['parm1']
value2 = request.params['parm2']

return Response( "responseData" )

---------------

Hope this helps in some way.

Uwe.

Markm

unread,
Jan 26, 2007, 12:33:02 PM1/26/07
to pylons-discuss
Many thanks folks!
I'll try use this here.
Marcus

On 26 jan, 03:45, Uwe Feldtmann <u...@microshare.com.au> wrote:
> Markm wrote:
> > I am just begining here ... do you mind if I see that your complete (I
> > mean your controller and defs) code? For eg, I did not understand this
> > piece of code:
>
> > I'm fighting with the same problem and it'll be very useful if I could

> > learn with you!Hi Markus.


>
> I see that Bob Ippolito has already explained the function() { ... }
> definition - thanks Bob.
>
> Most of the code I used was only to test out sending data back and forth

> between the server and the browser withoutusinghtml forms and would be


> more confusing than it's worth so I'll sum up what I've learned from
> experimentation and with the patient assistance of others on this list
> (thank you all). If anything here is incorrect or could be done better
> then please let us all know.
>
> Simple rules I read somewhere:-
> Use GET if the data you are sending to the server is less than 512 bytes
> and can be sent on the url.
> Use the POST method if the data is more than 512 bytes or needs to be
> more secure.
>
> ---------------
>

> Sending from the browserusingGET:-


>
> function toServerByGET(url, fct) {
> xmlhttp = new XMLHttpRequest();
>
> xmlhttp.onreadystatechange = function() {
> if ( xmlhttp.readyState==4 ) {
> fct( xmlhttp.responseText );
> }
> };
>
> xmlhttp.open("GET",url);
> xmlhttp.send(null);
>
> }and is called like this
>
> urlAndData = "/controllerName/methodName/?parm1=" + value1 +
> "&amp;parm2=" + value2;
> toServer( urlAndData, responseHandler )
>
> ---------------
>

> Sending from the browserusingPOST:-

Reply all
Reply to author
Forward
0 new messages