Reputation: 5844
I'm maintaining someone else's old CherryPy code, and have an interesting situation I'm trying to understand. Consider this code which demonstrates it:
import cherrypy
class HelloWorld(object):
def index(self, name):
html = """<form method="post">
<input type="text" name="name" value="%s" /></form>
Hello %s!""" % (name, name)
return html
index.exposed = True
cherrypy.quickstart(HelloWorld())
Run it with python hello.py
and go to http://127.0.0.1:8080/?name=foo. The output is a text input box with "foo" followed by "Hello foo!".
But if I edit the text in the box and replace it with "bar" and hit enter (submitting the form), the result is not "bar" in the input box and "Hello bar!" below but (apologies for ascii art input box):
+---------------------+
| [u'foo', u'bar'] |
+---------------------+
Hello [u'foo', u'bar']!
It seems that CherryPy is combining the URL querystring "name" argument value with the form "name" value submitted in the body of the POST request and providing a list with the two values to the exposed index() method.
From what I can tell of the code I'm maintaining, it didn't always work this way. So that leads to my two questions:
(I'm not intimately familiar with the CherryPy documentation, but I couldn't find the answer there.)
Upvotes: 2
Views: 961
Reputation: 25234
I don't think it was the change in 3.x series. You can directly access GET and POST params like in the following snippet. However using unique names is more advised and less error-prone way.
import urlparse
import cherrypy
class HelloWorld:
@cherrypy.expose
def index(self, name):
postParams = cherrypy.request.body.params
getParams = urlparse.parse_qs(cherrypy.request.query_string)
print(postParams, getParams)
# ({u'name': u'bar'}, {'name': ['foo']})
Good research @TimB. By skipping through the codebase I couldn't find where the thing happens. And yes CherryPy 3.2+ is like the series on its own.
CherryPy is quite configurable, and your case is not an exception, when you're able to deal with its notions. Specifically you can write a CherryPy tool to override the mixed request params with POST params. It's just a few lines.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urlparse
import cherrypy
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8
},
'/' : {
'tools.postoverride.on' : True,
}
}
def postOverride():
cherrypy.request.params.update(cherrypy.request.body.params)
cherrypy.tools.postoverride = cherrypy.Tool('before_handler', postOverride)
class HelloWorld:
@cherrypy.expose
def index(self, name):
html = """<form method="post">
<input type="text" name="name" value="%s" /></form>
Hello %s!""" % (name, name)
return html
if __name__ == '__main__':
cherrypy.quickstart(HelloWorld(), '/', config)
Upvotes: 1
Reputation: 5844
I've found the answer to question 1 I posed.
The behavior changed with the release of CherryPy 3.2. The actual change was made in git commit e05feef4fee7df1ee5d25d11393f872c9ef12510 (hg:3b92b5aa76f9) on 31 May 2009, which was when the _cpreqbody module was switched in in place of the old process_body() method.
The code used to do this (where self.params is a dict, already containing the params from the query string):
self.body_params = p = httputil.params_from_CGI_form(forms)
self.params.update(p)
From 3.2, it now does this (where key
and value
are from the POST body being processed):
if key in params:
if not isinstance(params[key], list):
params[key] = [params[key]]
params[key].append(value)
else:
params[key] = value
It looks like there's no easy answer to my question 2, so I'll have to use cherrypy.request.body_params
when I know that's where the value I'm after will be found.
Upvotes: 2
Reputation: 7654
I do get your expected result. I imagine there is something wrong with your code.
In particular you use
% (name, name)
Which CANNOT resolve to two different values.
Upvotes: 0