CherryPy 将查询字符串值与 POST 正文中的表单值相结合

CherryPy combining querystring value with form value in POST body

我正在维护其他人的旧 CherryPy 代码,并且有一个有趣的情况我正在尝试了解。考虑一下演示它的代码:

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())

运行 它与 python hello.py 并转到 http://127.0.0.1:8080/?name=foo。输出是一个带有 "foo" 后跟 "Hello foo!".

的文本输入框

但是如果我编辑框中的文本并将其替换为 "bar" 并按回车键(提交表单),结果在输入框中不是 "bar" 而 "Hello bar!" 下面但是(为 ascii 艺术输入框道歉):

 +---------------------+
 | [u'foo', u'bar']    |
 +---------------------+

 Hello [u'foo', u'bar']!

CherryPy 似乎将 URL 查询字符串 "name" 参数值与在 POST 请求正文中提交的表单 "name" 值结合起来,并提供了一个列表将两个值传递给公开的 index() 方法。

据我所知,我正在维护的代码并不总是这样工作。所以这引出了我的两个问题:

  1. CherryPy 是否在 3.7.0 之前的某个版本(这是我正在测试的版本)中更改了它处理这种情况的方式?
  2. 我可以配置 CherryPy 恢复到原来的行为,或者至少让 POST 值覆盖查询字符串值吗?

(我对 CherryPy 文档不是很熟悉,但在那里找不到答案。)

我确实得到了你预期的结果。我想你的代码有问题。

你特别使用

% (name, name)

无法解析为两个不同的值。

我不认为这是 3.x 系列的变化。您可以直接访问 GET 和 POST 参数,如以下代码段所示。但是,更建议使用唯一名称且不易出错。

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

更新

很好的研究@TimB。通过跳过代码库,我找不到事情发生的地方。是的,CherryPy 3.2+ 就像系列本身一样。

CherryPy 是完全可配置的,当您能够处理它的概念时,您的情况也不例外。具体来说,您可以编写一个 CherryPy 工具来使用 POST 参数覆盖混合请求参数。就几行而已。

#!/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)

我找到了我提出的问题 1 的答案。

随着 CherryPy 3.2 的发布,行为发生了变化。实际更改是在 2009 年 5 月 31 日的 git commit e05feef4fee7df1ee5d25d11393f872c9ef12510 (hg:3b92b5aa76f9) 中进行的,当时 _cpreqbody 模块被替换为旧的 process_body() 方法。

用于执行此操作的代码(其中 self.params 是一个字典,已经包含查询字符串中的参数):

        self.body_params = p = httputil.params_from_CGI_form(forms)
        self.params.update(p)

从 3.2 开始,它现在这样做(其中 keyvalue 来自正在处理的 POST 正文):

        if key in params:
            if not isinstance(params[key], list):
                params[key] = [params[key]]
            params[key].append(value)
        else:
            params[key] = value

我的问题 2 似乎没有简单的答案,所以我必须使用 cherrypy.request.body_params 当我知道那是我所追求的价值所在时。