允许用户删除他们的帐户导致 Flask 中出现 400 Bad Request
Allowing users to delete their accounts leads to 400 Bad Request in Flask
我在设置用户帐户删除选项时遇到问题。我已经设置了一个 Bootstrap 模式来弹出保证,我希望 'Delete' 按钮从数据库中删除当前用户的帐户,注销用户,并将用户重定向到主页即时消息。但是,当我单击该按钮时,它会抛出 400 Bad Request Error。不知道是 POST 方法的原因还是别的什么原因,所以我在这里,请求你的帮助。我在我的应用程序中使用 PostgreSQL 以及 SQLAlchemy 和 Flask-Login(对于 current_user
)。这是我第一次设置这样的东西,如果我的问题不够清楚,请告诉我,以便我提供更多详细信息。另外,如果您对我如何优化它有什么建议,我将非常感激。相关代码如下:
模态中的按钮:
<form action="{{ url_for('user.delete_account') }}" method="POST">
<input type="submit" id="delete" name="delete" value="Delete" class="btn btn-danger">
</form>
路由和删除函数:
@user.route('/delete', methods=['POST'])
@login_required
def delete_account():
if request.method == 'POST':
if request.form['delete'] == 'Delete':
current_user.delete()
flash('Your account has been successfully deleted.', 'success')
return redirect(url_for('core.home'))
我真的希望我的问题足够清楚,因为我连续 10 多个小时都被这个问题困住了,我需要帮助。提前致谢。
编辑:事实证明问题是可能来自全局安装的 CSRF 保护层(来自 Flask-WTF).如果您甚至到达您的处理程序,则进行调试将极大地帮助创建一个最小的可重现示例。
原始最佳猜测答案:您几乎肯定会从 request.form['delete']
收到 400 Bad Request 错误 - 当您尝试获取值时,Flask 会引发此类错误在未发送到服务器的 request.{form, args, values}
中(引发的错误也是 KeyError
的子类型,普通 Python 字典在相同情况下会引发:
({ "x": 123 })["y"] # Raises a KeyError
request.form["not-in-form"] # Raises a BadRequest error
所以现在的问题是,"why is 'delete' not in the form request?"。答案几乎肯定是因为您正在 JavaScript 中捕获此表单的提交事件并以编程方式提交表单......并且不包括按钮的值。包括必要的参数或者根本不检查表单值(甚至不检查方法,因为您已经将路由限制为仅接收 POST
)。
@user.route('/delete', methods=['POST'])
@login_required
def delete_account():
current_user.delete()
flash('Your account has been successfully deleted.', 'success')
return redirect(url_for('core.home'))
找到解决方案:
所以基本上,我对代码进行了一些尝试并找到了解决方案。这是相关代码:
首先,我创建了一个带有隐藏 CSRF 令牌的 Jinja2 宏:
{%- macro form_tag(endpoint, fid='', css_class='', method='post') -%}
<form action="{{ url_for(endpoint, **kwargs) }}" method="{{ method }}"
id="{{ fid }}" class="{{ css_class }}" role="form">
{{ form.hidden_tag() }}
{{ caller () }}
</form>
{%- endmacro -%}
之后我创建了最简单的形式:
from flask_wtf import Form
from wtforms import SubmitField
class DeleteUserForm(Form):
delete = SubmitField('Delete')
之后,我将此添加到 Bootstrap 模态:
{% import 'macros/form.html' as f with context %}
{% call f.form_tag('user.delete') %}
<button type="submit" class="btn btn-danger">Delete</button>
{% endcall %}
最后,我在 views.py 文件中修改了路由:
@user.route('/settings/delete', methods=['POST'])
@login_required
def delete():
form = DeleteUserForm()
if form.validate_on_submit():
current_user.delete()
flash('Your account has been successfully deleted. Hope to see you again.', 'success')
return redirect(url_for('user.login'))
return render_template('user/login.html', form=form)
这个解决方案是我能做的最好的。我希望这也会对其他人有所帮助。哦,我差点忘了,因为我正在编辑我的 settings.html
文件,我还必须在页面的 /settings
路由中传递 form=form
参数:
@user.route('/settings')
@login_required
def settings():
form = DeleteUserForm()
return render_template('user/settings.html', form=form)
就是这样。也感谢回复的大佬们,感激不尽
我在设置用户帐户删除选项时遇到问题。我已经设置了一个 Bootstrap 模式来弹出保证,我希望 'Delete' 按钮从数据库中删除当前用户的帐户,注销用户,并将用户重定向到主页即时消息。但是,当我单击该按钮时,它会抛出 400 Bad Request Error。不知道是 POST 方法的原因还是别的什么原因,所以我在这里,请求你的帮助。我在我的应用程序中使用 PostgreSQL 以及 SQLAlchemy 和 Flask-Login(对于 current_user
)。这是我第一次设置这样的东西,如果我的问题不够清楚,请告诉我,以便我提供更多详细信息。另外,如果您对我如何优化它有什么建议,我将非常感激。相关代码如下:
模态中的按钮:
<form action="{{ url_for('user.delete_account') }}" method="POST">
<input type="submit" id="delete" name="delete" value="Delete" class="btn btn-danger">
</form>
路由和删除函数:
@user.route('/delete', methods=['POST'])
@login_required
def delete_account():
if request.method == 'POST':
if request.form['delete'] == 'Delete':
current_user.delete()
flash('Your account has been successfully deleted.', 'success')
return redirect(url_for('core.home'))
我真的希望我的问题足够清楚,因为我连续 10 多个小时都被这个问题困住了,我需要帮助。提前致谢。
编辑:事实证明问题是可能来自全局安装的 CSRF 保护层(来自 Flask-WTF).如果您甚至到达您的处理程序,则进行调试将极大地帮助创建一个最小的可重现示例。
原始最佳猜测答案:您几乎肯定会从 request.form['delete']
收到 400 Bad Request 错误 - 当您尝试获取值时,Flask 会引发此类错误在未发送到服务器的 request.{form, args, values}
中(引发的错误也是 KeyError
的子类型,普通 Python 字典在相同情况下会引发:
({ "x": 123 })["y"] # Raises a KeyError
request.form["not-in-form"] # Raises a BadRequest error
所以现在的问题是,"why is 'delete' not in the form request?"。答案几乎肯定是因为您正在 JavaScript 中捕获此表单的提交事件并以编程方式提交表单......并且不包括按钮的值。包括必要的参数或者根本不检查表单值(甚至不检查方法,因为您已经将路由限制为仅接收 POST
)。
@user.route('/delete', methods=['POST'])
@login_required
def delete_account():
current_user.delete()
flash('Your account has been successfully deleted.', 'success')
return redirect(url_for('core.home'))
找到解决方案:
所以基本上,我对代码进行了一些尝试并找到了解决方案。这是相关代码:
首先,我创建了一个带有隐藏 CSRF 令牌的 Jinja2 宏:
{%- macro form_tag(endpoint, fid='', css_class='', method='post') -%}
<form action="{{ url_for(endpoint, **kwargs) }}" method="{{ method }}"
id="{{ fid }}" class="{{ css_class }}" role="form">
{{ form.hidden_tag() }}
{{ caller () }}
</form>
{%- endmacro -%}
之后我创建了最简单的形式:
from flask_wtf import Form
from wtforms import SubmitField
class DeleteUserForm(Form):
delete = SubmitField('Delete')
之后,我将此添加到 Bootstrap 模态:
{% import 'macros/form.html' as f with context %}
{% call f.form_tag('user.delete') %}
<button type="submit" class="btn btn-danger">Delete</button>
{% endcall %}
最后,我在 views.py 文件中修改了路由:
@user.route('/settings/delete', methods=['POST'])
@login_required
def delete():
form = DeleteUserForm()
if form.validate_on_submit():
current_user.delete()
flash('Your account has been successfully deleted. Hope to see you again.', 'success')
return redirect(url_for('user.login'))
return render_template('user/login.html', form=form)
这个解决方案是我能做的最好的。我希望这也会对其他人有所帮助。哦,我差点忘了,因为我正在编辑我的 settings.html
文件,我还必须在页面的 /settings
路由中传递 form=form
参数:
@user.route('/settings')
@login_required
def settings():
form = DeleteUserForm()
return render_template('user/settings.html', form=form)
就是这样。也感谢回复的大佬们,感激不尽