如何在 Pyramid 中读取 HTTP POST 参数?
支付小周
结论
对于 Content-Type: application/x-www-form-urlencoded 的 POST 请求,应通过 request.POST 读取表单参数,不要使用 request.json_body。
form_data = request.POST
payment_id = form_data.get("id")
如需普通 dict,可以直接转换:
data = dict(request.POST)
示例请求解析后的结果相当于:
{
"id": "tr_uH4yPGBahB"
}
原因
request.json_body 会使用 JSON 解析器处理请求正文,而下面这段正文:
id=tr_uH4yPGBahB
是 URL 编码的表单数据,并非合法 JSON,因此会触发 ValueError 或相应的 JSON 解码异常。
合法的 JSON 正文应类似:
{"id": "tr_uH4yPGBahB"}
同时需要配合:
Content-Type: application/json
当前请求使用的是:
Content-Type: application/x-www-form-urlencoded
Pyramid 会按照表单编码规则解析正文,并通过 request.POST 提供解析结果。
读取单个参数
from pyramid.view import view_config
@view_config(route_name="payment_webhook", renderer="json")
def payment_webhook(request):
payment_id = request.POST.get("id")
if payment_id is None:
request.response.status_code = 400
return {"error": "Missing form field: id"}
return {"id": payment_id}
也可以通过下标读取:
payment_id = request.POST["id"]
两种写法的区别如下:
request.POST.get("id")在参数不存在时返回None。request.POST["id"]在参数不存在时抛出KeyError。
处理外部 webhook 时,通常更适合使用 get(),再主动校验参数是否存在。
转换为 dict
request.POST 是一个类似字典的多值映射对象,可以直接转为普通字典:
data = dict(request.POST)
转换后即可按普通字典读取:
payment_id = data.get("id")
完整示例:
@view_config(route_name="payment_webhook", renderer="json")
def payment_webhook(request):
data = dict(request.POST)
request.registry.settings["logger"].debug(
"Receiving form webhook data: %r",
data,
)
return {
"received": True,
"id": data.get("id"),
}
实际项目中,不应在日志里记录密码、访问令牌、签名密钥或完整的支付信息。
处理同名参数
表单允许同一个字段出现多次:
tag=python&tag=pyramid
直接转换为普通 dict 时,可能只保留其中一个值。要保留所有值,应使用 getall():
tags = request.POST.getall("tag")
返回结果为:
["python", "pyramid"]
也可以将其转换为每个字段对应一个值列表的字典:
data = {
key: request.POST.getall(key)
for key in request.POST
}
request.POST 与 request.params 的区别
如果只需要读取 POST 表单正文,请使用:
request.POST
request.params 通常会合并查询字符串参数和表单参数。例如:
POST /webhook?id=query-value
Content-Type: application/x-www-form-urlencoded
id=form-value
此时使用 request.params,容易混淆参数来自查询字符串还是表单正文。对于 webhook 或签名验证场景,明确使用 request.POST 更合适。
这个请求可以直接这样读取:
data = dict(request.POST)
payment_id = request.POST.get("id")
备注:内容仅供参考。
