如何使用Flask和Flask-login传递"下一个"URL?

Dav*_*ite 15 python forms flask flask-login

Flask-login的文档讨论了处理"下一个"URL的问题.这个想法似乎是:

  1. 用户去了 /secret
  2. 用户重定向到登录页面(例如/login)
  3. 成功登录后,用户将被重定向回 /secret

我发现使用Flask-login的唯一半完整示例是https://gist.github.com/bkdinoop/6698956.它很有用,但由于它不包含HTML模板文件,我看到我是否可以将它们重新创建为自我训练练习.

这是/secret/login部分的简化版本:

@app.route("/secret")
@fresh_login_required
def secret():
    return render_template("secret.html")

@app.route("/login", methods=["GET", "POST"])
def login():
    <...login-checking code omitted...>
    if user_is_logged_in:
        flash("Logged in!")
        return redirect(request.args.get("next") or url_for("index"))
    else:
        flash("Sorry, but you could not log in.")
        return render_template("login.html")
Run Code Online (Sandbox Code Playgroud)

这是login.html:

<form name="loginform" action="{{ url_for('login') }}" method="POST">
Username: <input type="text" name="username" size="30" /><br />
Password: <input type="password" name="password" size="30" /><br />
<input type="submit" value="Login" /><br />
Run Code Online (Sandbox Code Playgroud)

现在,当用户访问时/secret,他被重定向到/login?next=%2Fsecret.到目前为止,非常好 - 查询字符串中的"next"参数.

但是,当用户提交登录表单时,他将被重定向回索引页面,而不是返回到/secretURL.

我猜测原因是因为传入URL上的"next"参数没有合并到登录表单中,因此在处理表单时不会作为变量传递.但是正确的方法是什么解决这个?

一个解决方案似乎有效 - <form>从中更改标签

<form name="loginform" action="{{ url_for('login') }}" method="POST">
Run Code Online (Sandbox Code Playgroud)

至:

<form name="loginform" method="POST">
Run Code Online (Sandbox Code Playgroud)

删除"action"属性后,浏览器(至少是Windows上的Firefox 45)会自动使用当前URL,从而继承?next=%2Fsecret查询字符串,该字符串会成功将其发送到表单处理处理程序.

但是省略了"action"表单属性并让浏览器在正确的解决方案中填充它?它适用于所有浏览器和平台吗?

或者Flask或Flask-login是否打算以不同的方式处理?

tim*_*kro 20

如果需要在表单中指定其他操作属性,则无法使用Flask-Login提供的下一个参数.无论如何,我建议将端点而不是url放入url参数,因为它更容易验证.这是我正在处理的应用程序的一些代码,也许这可以帮助你.

覆盖Flask-Login的未授权处理程序,以使用端点而不是下一个参数中的url:

@login_manager.unauthorized_handler
def handle_needs_login():
    flash("You have to be logged in to access this page.")
    return redirect(url_for('account.login', next=request.endpoint))
Run Code Online (Sandbox Code Playgroud)

也可以request.endpoint在您自己的网址中使用:

{# login form #}
<form action="{{ url_for('account.login', next=request.endpoint) }}" method="post">
...
</form>
Run Code Online (Sandbox Code Playgroud)

如果存在并且有效,则在下一个参数中重定向到端点,否则重定向到回退.

def redirect_dest(fallback):
    dest = request.args.get('next')
    try:
        dest_url = url_for(dest)
    except:
        return redirect(fallback)
    return redirect(dest_url)

@app.route("/login", methods=["GET", "POST"])
def login():
    ...
    if user_is_logged_in:
        flash("Logged in!")
        return redirect_dest(fallback=url_for('general.index'))
    else:
        flash("Sorry, but you could not log in.")
        return render_template("login.html")
Run Code Online (Sandbox Code Playgroud)

  • 在我使用的形式中:next=request.args.get("next")。同样在redirect_dest中,它可能应该是 dest = request.args.get("next") (2认同)
  • @2ni 在我的示例中,表单显示在每个页面的顶部,如果您想要一个单独的登录页面,您应该使用`next=request.args.get("next")`。修正了你第二次提到的问题,谢谢。 (2认同)
  • 如果您的网址可能包含变量,请使用“request.path”。 (2认同)