Flask页面提交表单时出现Bad Request错误,求排查解决
解决Flask双表单提交的Bad Request问题
咱们一步一步来排查和解决你的问题:
问题根源分析
- 表单标识方式错误:你给
<form>标签加了name="btn"和value="project",但HTML的<form>标签本身并不支持这两个属性!浏览器提交表单时,只会发送表单内部带有name属性的控件的值,所以你的request.form['btn']在第二个表单提交时根本找不到对应的键,直接触发KeyError,导致Flask返回Bad Request。 - 第二个表单的select缺少name属性:你的第二个下拉框只有
id="subsystem_value",没有name属性,就算表单提交了,Flask也拿不到subsystem_value这个参数。 - 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
相关产品推荐
相关产品推荐

