我对 PHP Post/Redirect/Get 感到困惑
Posted
技术标签:
【中文标题】我对 PHP Post/Redirect/Get 感到困惑【英文标题】:I am confused about PHP Post/Redirect/Get 【发布时间】:2011-12-02 15:55:58 【问题描述】:在关于防止 php 表单重新提交的 article 中,我阅读了以下内容:
(不引用)这可能是接收表单数据的页面,例如称为“form.php”:
<form action="submit.php">
<input type="text" name="user" required />
<input type="password" name="pass" required />
<input type="submit" value="Log in" />
</form>
因此,处理 POST 数据的页面将被称为“submit.php”。如果登录正确,此代码将运行:
header('Location: /login/form.php?success=true');
但是,用户不能直接导航到上面的 URL 吗?另外,GET 变量的目的是什么?我不能只在 form.php 中有一个脚本来检查用户是否登录吗?
在 submit.php 中,我应该将登录的用户名保存为 $_SESSION['username'],然后在 form.php 中检查是否 isset()?此外,由于其中包含“成功”的 URL 不是很漂亮,再次重定向用户是否经济?我应该使用 PHP header() 还是 javascript window.location.href? 如您所见,我有点困惑。
感谢您的帮助。
【问题讨论】:
“但是,用户不能直接导航到上面的 URL 吗?” - - 是的,他能。那么? 你是对的,没有 POST 数据不会造成任何伤害。 网址中的?success
并没有那么难看。我一直都在使用这样的东西,因为大多数人都不注意它,而且它真的很容易。
我假设该示例中查询字符串的目的是供form.php
用来确定它是否应该显示成功消息。如果你愿意,你可以在会话中做这种事情。重要的部分是有一个重定向,所以重新加载不会重新执行 POST,只是对成功页面的无害 GET。
@user805556:它是任何类型表单的通用解决方案,例如添加评论表单。在这种情况下,检查消息是否已成功添加并不像if (isset($_SESSION...
那样容易
【参考方案1】:
但是,用户不能直接导航到上面的 URL 吗?
是的,他可以。不过这不会造成任何不良后果。
另外,GET 变量的用途是什么?
要有一些标志表示表单已成功处理,您需要祝贺用户。
我不能只在 form.php 中有一个脚本来检查用户是否登录吗?
嗯,您可以按照您喜欢的方式保留您的代码。没有任何强烈的要求
在 submit.php 中,我应该将登录的用户名保存为 $_SESSION['username'],然后在 form.php 中检查 ifisset() 吗?
如果您需要在当前会话中保留它 - 是的,这样做。
此外,由于其中包含“成功”的 URL 不是很漂亮,再次重定向用户是否经济?
重定向到哪里。重定向是相当便宜的东西。
我应该使用 PHP header() 还是 Javascript window.location.href?
你绝对应该在 php 中这样做,否则你会遇到你试图避免遵循 PRG 方式的麻烦。
【讨论】:
好的,这有点清楚了。但是,由于我从不使用单独的登录页面,而是在每个页面上都有它们,我应该重定向到这个:? “submit.php?page=whatever.php”,然后使用该变量重定向回该特定页面?另外,我怎样才能在 PHP 中找到当前页面,以便我可以自动化我上面建议的过程?谢谢。 @user805556:只需将重定向网址作为任何其他参数传递,并在处理后 - 重定向回它。您可以在$_SERVER
找到当前 url(或者更准确地说是当前 url 的片段)【参考方案2】:
PRG 或 Post/Redirect/Get 只是一种可以用来防止消息框的模式。您如何详细使用它(本文仅提供一般性建议)取决于您的需求。
如果您想在 cookie、会话或 get 变量中标记成功 Flash 消息,这完全取决于您。顺便说一句,第二次重定向无济于事,如果你玩弄它,你就会明白这一点。
唯一重要的部分是,在您收到 POST 请求后,您进行重定向。然后,用户仍然可以在历史记录中前后移动,而不会被要求重新提交 POST 数据。
该模式有效并且是一件好事。就在两天前,我又做了一次,一步一步的 weppapp 安装程序在浏览器界面中导航要好得多。
关于您的重定向
这段代码是错误的:
header('Location:/login/form.php?success=true');
首先,冒号后面需要有一个空格:
header('Location: /login/form.php?success=true');
那么地址必须是绝对URI,它必须包含完整的URL:
header('Location: http://example.com/login/form.php?success=true');
在header()
旁边,您应该根据 RFC 提供消息正文,许多所谓的“网络开发人员”甚至都不知道:
$url = 'http://example.com/login/form.php?success=true';
header(sprintf('Location: %s', $url));
printf('<a href="%s">Moved</a>.', $url);
exit;
别忘了出口。当然,这几乎是在重新发明***,而是安装 PHP 的 http 扩展,然后执行以下操作:
http_redirect('/login/form.php?success=true');
你发现nifty helper here。
回顾一下:重要的是您在发布后进行重定向。其他一切,比如传递一个变量,完全取决于你想怎么做。
【讨论】:
@user805556:请点击:sprintf
不,它不需要是绝对 URI,甚至是根路径。【参考方案3】:
是的,您永远不应该依赖 GET 变量(甚至是隐藏的 POST 变量)说“当然,让我进入,我是有效用户!”。
就我个人而言,我会从链接中删除 GET 信息并仅依赖会话变量。记得放置一个 'session_start();'如果您使用 PHP 激活会话,则作为第一行代码。
对于提交.php:
<?php
session_start();
if ($_POST['user'] && $_POST['pass']) // Make sure both variable are set
if (your_method)
// Code to check if the user and pass are valid however you plan
$_SESSION['user'] = $_POST['user'];
$_SESSION['loggedin'] = time();
header('Location: form.php'); // Either way, pass or fail, return to form.php
exit();
?>
然后在form.php中:
<?php
session_start();
$activeuser = false;
if ($_SESSION['user'] && $_SESSION['loggedin'] < (time()+600))
// Check if the user exists and the last access was with in 10 minutes.
$_SESSION['loggedin'] = time(); // If so, keep them up to date!
$activeuser = true;
if ($activeuser)
// whatever should show to someone logged in
else
// Show log in form
?>
另外,你可能已经知道了,但是默认的传输方式是GET,所以一定要在form标签中指定method="post"。
如果需要,通常最好使用 header() 进行重定向,因为 Javascript 是客户端并且可以避免这可能会破坏您网站运行的意图。
【讨论】:
【参考方案4】:正如您链接到的文章所指出的那样,POST/REDIRECT/GET 背后的主要思想是避免用户重新提交数据(大部分时间)。通常,您不希望相同的 POST(具有完全相同的数据)发生两次——事实上,在某些情况下,它最终可能会第二次执行某些操作(例如从信用卡中收取费用),这会很糟糕.
您在问题中询问的大部分内容都是实现细节(例如在重定向中发送 ?success 请求参数)。
在实践中,通常发生的情况是您的重定向成功。例如,如果用户的输入验证失败,您不重定向,而是重新显示表单以及相关的错误消息。
这是一个基本示例,全部在一个脚本中。我试图只包含重要的内容,尽可能少的无关内容。
login.php
<?php
/**
* ensure user supplied both username & password
* @return mixed true or an array of error messages
*/
function validate_login_values($vars)
$errors = array();
if (empty($vars['username'])) $errors[] = 'You must supply a username, genius.';
if (empty($vars['password'])) $errors[] = 'You must supply a password, dummy.';
if (empty($errors)) return true;
return $errors; // $errors must be an array.
if (! empty($_POST))
$validationResults = validate_login_values($_POST);
if ($validationResults === true)
// assume here that authenticate properly escapes it's arguments before sending them
// to the database.
if (authenticate($_POST['username'],$_POST['password']))
//GREAT SUCCESS! The user is now logged in. Redirect to home page
header("Location: /");
die();
$errors[] = 'Invalid username/password. Try again, slim";
else
$errors = $validationResults; // validate_login_values created errors.
?>
<h1>Log In, Friend!</h1>]
<?php
//display errors, if there were any
if (! empty($errors)): ?>
<div class="errors">Something went horribly wrong:
<ul><?php foreach($errors as $e) echo "<li>$e</li>"; ?></ul>
<div>
<?php endif; ?>
<form method="POST">
<!-- //username, password, and submit -->
</form>
【讨论】:
一个问题:当我使用 header() 重定向用户然后回显时,这些内容在哪里得到回显?在顶部?以上是关于我对 PHP Post/Redirect/Get 感到困惑的主要内容,如果未能解决你的问题,请参考以下文章
Flask post / redirect / get模式无法从错误中恢复
如果请求和响应的 uri 相同,为啥 Microsoft Edge 在调用 POST/REDIRECT/GET 方法时发送空的 http-referer?