TimB
TimB

Reputation: 5844

CherryPy combining querystring value with form value in POST body

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:

  1. Did CherryPy change how it handles this situation in some version prior to 3.7.0 (which is what I'm testing with)?
  2. Can I configure CherryPy to revert to that old behavior, or at least to have the POST value override the querystring value?

(I'm not intimately familiar with the CherryPy documentation, but I couldn't find the answer there.)

Upvotes: 2

Views: 961

Answers (3)

saaj
saaj

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']})

Update

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

TimB
TimB

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

The Tahaan
The Tahaan

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

Related Questions