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

Flask页面提交表单时出现Bad Request错误,求排查解决

解决Flask双表单提交的Bad Request问题

咱们一步一步来排查和解决你的问题:

问题根源分析

  1. 表单标识方式错误:你给<form>标签加了name="btn"和value="project",但HTML的<form>标签本身并不支持这两个属性!浏览器提交表单时,只会发送表单内部带有name属性的控件的值,所以你的request.form['btn']在第二个表单提交时根本找不到对应的键,直接触发KeyError,导致Flask返回Bad Request。
  2. 第二个表单的select缺少name属性:你的第二个下拉框只有id="subsystem_value",没有name属性,就算表单提交了,Flask也拿不到subsystem_value这个参数。
  3. SQL注入风险:额外提一句,你用format拼接SQL语句的做法非常危险,很容易被SQL注入攻击,后面会给你修正。

修正后的代码示例

1. HTML模板部分(ip.html)

<!-- 第一个表单:选择项目 -->
<form action="/ip" method="POST">
  <!-- 隐藏字段,用来标识这个是项目选择表单的提交 -->
  <input type="hidden" name="form_type" value="project">
  <label>Select Project : </label>
  <select class="form-control" style="width: 50%;display:inline-block" name="project_name">
    <option></option>
    <option value="k">Ki</option>
    <option value="s">S</option>
    <option value="l">L</option>
  </select>
  <button type="submit" class="btn btn-primary m-b-10 m-l-5" style="display:inline-block">Fetch Details</button>
</form>

<!-- 第二个表单:选择子系统,仅当有数据时显示 -->
{% if data %}
<form method="POST" action="/ip">
  <!-- 隐藏字段,标识这个是子系统选择表单的提交 -->
  <input type="hidden" name="form_type" value="dlvr">
  <select id="subsystem_value" name="subsystem_value" onchange="this.form.submit()">
    {% for i in data %}
      {% for k in i %}
        <option value="{{ k }}">{{ k }}</option>
      {% endfor %}
    {% endfor %}
  </select>
</form>
{% endif %}

2. Flask视图函数部分

@auth.route('/ip', methods=['POST', 'GET'])
def ip():
    if request.method == 'POST':
        # 通过隐藏字段判断是哪个表单提交的
        form_type = request.form.get('form_type')
        if form_type == "project":
            project = request.form.get('project_name')
            if not project:
                # 处理用户未选择项目的情况
                return render_template('ip.html', error="请选择一个项目")
            
            c, conn = connection()
            # 用参数化查询避免SQL注入!
            subsystem_query = "SELECT distinct sub from ip where project=%s"
            del_query = "SELECT distinct del from ip where project=%s"
            
            c.execute(del_query, (project,))
            data = c.fetchall()
            
            c.execute(subsystem_query, (project,))
            subsystem = c.fetchall()
            
            sub = [row[0] for row in subsystem]  # 简化列表推导
            conn.close()
            return render_template('ip.html', data=data, sub=sub)
        elif form_type == "dlvr":
            subsystem = request.form.get('subsystem_value')
            if not subsystem:
                # 处理未选择子系统的情况
                return render_template('ip.html', error="请选择一个子系统")
            # 这里可以添加你拿到subsystem后的逻辑,比如查询数据等
            return render_template('ip.html', selected_subsystem=subsystem)
        else:
            # 处理未知的表单类型
            return render_template('ip.html', error="无效的请求")
    else:
        return render_template('ip.html')

关键改进点说明

  • 用隐藏字段区分表单:每个表单里加一个<input type="hidden" name="form_type" value="xxx">,这样Flask就能准确判断是哪个表单提交的,避免KeyError。
  • 给select添加name属性:表单控件只有带name属性,提交后才能在request.form里获取到对应的值。
  • 参数化查询:把SQL语句里的占位符换成%s(适配MySQLdb等驱动),然后把参数作为元组传给execute方法,彻底避免SQL注入风险。
  • 增加参数校验:用request.form.get()代替直接取键,就算参数不存在也不会报错,还可以添加友好的错误提示。

这样修改后,你的第二个表单onchange提交时就不会再出现Bad Request错误了,同时代码也更安全健壮。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:25:14