You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WSGI应用接收JSON Payload触发AssertionError,寻求技术指导

问题描述

搭建基于Python + WSGI的应用,用于接收云端JSON格式Payload并最终发送邮件,环境搭配NGINX。测试时用curl发送JSON数据触发AssertionError,核心是JSON数据捕获失败。

代码片段

from wsgiref.simple_server import make_server

import cgi
import subprocess
from email.message import EmailMessage
import json

def sendEmail(from_addr, to_addrs, msg_subject, msg_body):
    msg = EmailMessage()
    msg.set_content(msg_body)
    msg['From'] = from_addr
    msg['To'] = to_addrs
    msg['Subject'] = msg_subject
    sendmail_location = "/usr/sbin/sendmail"
    subprocess.run([sendmail_location, "-t", "-oi"], input=msg.as_bytes())

def is_post_request(environ):
    if environ['REQUEST_METHOD'].upper() != 'POST':
        return False
    content_type = environ.get('CONTENT_TYPE', 'application/x-www-form-urlencoded')
    print("I am printing here " + content_type)
    return (content_type.startswith('application/x-www-form-urlencoded' or content_type.startswith('multipart/form-data' or content_type.startswith('application/json'))))

def get_post_form(environ):
    print ("Printing environ" + str(environ))
    assert is_post_request(environ)
    input = environ['wsgi.input']
    post_form = environ.get('wsgi.post_form')
    if (post_form is not None
        and post_form[0] is input):
        return post_form[2]
    # This must be done to avoid a bug in cgi.FieldStorage
    environ.setdefault('QUERY_STRING', '')
    fs = cgi.FieldStorage(fp=input,
                          environ=environ,
                          keep_blank_values=1)
    new_input = InputProcessed()
    post_form = (new_input, input, fs)
    environ['wsgi.post_form'] = post_form
    environ['wsgi.input'] = new_input
    return fs

class InputProcessed(object):
    def read(self, *args):
        raise EOFError('The wsgi.input stream has already been consumed')
    readline = readlines = __iter__ = read


def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    user = get_post_form(environ).getvalue('user')
    password = get_post_form(environ).getvalue('password')
    output = 'user is: '+user+' and password is: '+password
    return [output.encode()]

测试命令

curl --header "Content-Type: application/json" --request POST --data '{"user":"abc","password":"xyz"}' http://localhost:8580

错误信息

Printing environ{'QUERY_STRING': '', 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'application/json', 'CONTENT_LENGTH': '31', 'REQUEST_URI': '/', 'PATH_INFO': '/', 'DOCUMENT_ROOT': '/usr/share/nginx/html', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REQUEST_SCHEME': 'http', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': '43534', 'SERVER_PORT': '8580', 'SERVER_NAME': 'p0001.emea.net', 'HTTP_HOST': 'localhost:8580', 'HTTP_USER_AGENT': 'curl/7.61.1', 'HTTP_ACCEPT': '*/*', 'HTTP_CONTENT_TYPE': 'application/json', 'HTTP_CONTENT_LENGTH': '31', 'wsgi.input': <uwsgi._Input object at 0x7f34812cfb28>, 'wsgi.file_wrapper': <built-in function uwsgi_sendfile>, 'wsgi.version': (1, 0), 'wsgi.errors': <_io.TextIOWrapper name=2 mode='w' encoding='UTF-8'>, 'wsgi.run_once': False, 'wsgi.multithread': False, 'wsgi.multiprocess': True, 'wsgi.url_scheme': 'http', 'uwsgi.version': b'2.0.26', 'uwsgi.node': b'p0001.emea.net'}
I am printing here application/json
Traceback (most recent call last):
  File "./wsgi.py", line 61, in application
    user = get_post_form(environ).getvalue('user')
  File "./wsgi.py", line 36, in get_post_form
    assert is_post_request(environ)
AssertionError
[pid: 379691|app: 0|req: 1/1] 127.0.0.1 () {36 vars in 463 bytes} [Wed Jul 10 10:04:26 2024] POST / => generated 0 bytes in 1 msecs (HTTP/1.1 200) 1 headers in 44 bytes (0 switches on core 0)
解决方法

1. 修复is_post_request函数逻辑错误

原函数中return语句的逻辑嵌套错误导致application/json类型无法被识别,正确写法需对三种Content-Type分别做判断:

def is_post_request(environ):
    if environ['REQUEST_METHOD'].upper() != 'POST':
        return False
    content_type = environ.get('CONTENT_TYPE', 'application/x-www-form-urlencoded')
    print("I am printing here " + content_type)
    return (content_type.startswith('application/x-www-form-urlencoded') 
            or content_type.startswith('multipart/form-data') 
            or content_type.startswith('application/json'))

2. 新增JSON数据解析逻辑

cgi.FieldStorage无法处理JSON格式请求,需单独读取请求体并解析。修改原get_post_form函数为get_post_data,分支处理不同类型数据:

def get_post_data(environ):
    print ("Printing environ" + str(environ))
    assert is_post_request(environ)
    
    content_type = environ.get('CONTENT_TYPE', '')
    input_stream = environ['wsgi.input']
    
    # 检查是否已处理过请求体
    post_data = environ.get('wsgi.post_data')
    if post_data is not None:
        return post_data
    
    if content_type.startswith('application/json'):
        # 解析JSON数据
        content_length = int(environ.get('CONTENT_LENGTH', 0))
        json_data = json.loads(input_stream.read(content_length))
        environ['wsgi.post_data'] = json_data
        return json_data
    else:
        # 处理表单数据
        environ.setdefault('QUERY_STRING', '')
        fs = cgi.FieldStorage(fp=input_stream,
                              environ=environ,
                              keep_blank_values=1)
        environ['wsgi.post_data'] = fs
        # 标记输入流已处理
        environ['wsgi.input'] = InputProcessed()
        return fs

3. 修改application函数适配多数据类型

根据返回的数据类型(字典或FieldStorage)获取字段值,并预留邮件调用逻辑:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    post_data = get_post_data(environ)
    
    if isinstance(post_data, dict):
        # JSON格式数据
        user = post_data.get('user')
        password = post_data.get('password')
    else:
        # 表单格式数据
        user = post_data.getvalue('user')
        password = post_data.getvalue('password')
    
    # 根据payload中的method字段调用邮件发送函数
    if post_data.get('method') == 'email':
        # 替换为实际的发件人、收件人信息
        sendEmail('sender@example.com', 'recipient@example.com', 'Test Notification', f'User: {user}\nPassword: {password}')
    
    output = f'user is: {user} and password is: {password}'
    return [output.encode()]

内容的提问来源于stack exchange,提问作者Anirban Banerjee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 04:14:54