使用Post参数进行PHP重定向

30

我有一个网页。这个网页会将用户重定向到另一个网页,类似于以下方式:

<form method="post" action="anotherpage.php" id="myform">

    <?php foreach($_GET as $key => $value){
    echo "<input type='hidden' name='{$key}' value='{$value}' />";
    } ?>

</form>
<script>

    document.getElementById('myform').submit();

</script>

你看,我的做法是将GET参数转换为POST参数。不要告诉我这样做不好,我自己知道,而且这并不是我真正要做的事情,重要的是我从一个数组中收集数据,尝试通过POST提交到另一个页面。但如果用户关闭了JavaScript,它就无法工作。我需要知道的是:是否有一种方法可以通过PHP方式传递POST参数,以便可以使用PHP方式进行重定向(header('Location: anotherpage.php');)?

对于我来说,通过POST传递参数非常重要。我不能使用$_SESSION变量,因为网页位于另一个域上,因此$_SESSION变量不同。

无论如何,我只需要一种用PHP传递POST变量的方法 ^^

提前感谢!


你能否设计一个名为“点击继续”的提交按钮,在页面加载时使用 JavaScript 立即隐藏它?或者它可以放在<noscript />块中。这样做不太好,但可能会解决脚本无法工作的问题。 - Tom Haigh
使用POST数据重定向:https://dev59.com/Im035IYBdhLWcg3wMM4G - Eduardo Cuomo
8个回答

38

你可以重定向一个POST请求,并包含POST信息。但是,你需要显式返回HTTP状态码307。浏览器将302视为对GET的重定向,忽略原始方法。这在HTTP文档中明确说明:

在实际操作中,这意味着在PHP中,在重定向位置之前需要设置状态码:

    header('HTTP/1.1 307 Temporary Redirect');
    header('Location: anotherpage.php');
然而,根据HTTP规范,用户代理必须询问用户是否同意重新提交POST信息到新的URL。在实际情况下,Chrome不会询问,Safari也不会,但Firefox将向用户显示一个弹出框以确认重定向。根据您的操作限制,这可能没问题,但在一般情况下,它肯定会给最终用户带来混淆的潜力。

11

无法直接从服务器完成此操作,因为POST数据应该由浏览器发送。

但是您可以选择一种替代方案:

  • 在您的示例中自动提交填充的表单可能会起作用,但正如您所写的那样,这并不是一个好的实践,并且可能会让用户停留在一个空白页面。
  • 接收GET参数并使用curl(或任何体面的HTTP客户端)将其POST到第二个站点,然后将结果传输给浏览器。这称为代理,可以是一个很好的解决方案。
  • 在域之间进行会话共享,这可能不适用于所有设置,并且可能会很复杂。设置完成后,会话共享对PHP代码几乎透明。如果您需要在两个域之间进行多个通信,则可能值得这样做。

使用curl解决方案的示例,要运行的代码位于域1:

//retrieve GET parameters as a string like arg1=0&arg1=56&argn=zz
$data = $_SERVER['QUERY_STRING']; 

// Create a curl handle to domain 2
$ch = curl_init('http://www.domain2.com/target_script.php'); 

//configure a POST request with some options
curl_setopt($ch, CURLOPT_POST, true);
//put data to send
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);    
//this option avoid retrieving HTTP response headers in answer
curl_setopt($ch, CURLOPT_HEADER, 0);
//we want to get result as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//if servers support is and you want result faster, allow compressed response
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); 

//execute request
$result = curl_exec($ch);

//show response form domain 2 to client if needed
echo $result;

就这样,您的客户端浏览器甚至不会看到域2服务器,它只会得到来自该服务器的结果。如果要将客户端重定向到域,请使用经典的HTTP header。

header('Location: http://www.domain2.com');

当然,这是带有硬编码值的演示代码,你还需要注意以下两点:

  • 安全性:查询字符串应该被过滤或重新创建,以仅传输所需的参数,并且您应该断言服务器在域2返回了200个HTTP代码。
  • 应用程序逻辑在此部分需要进行一些调整:如果域2应用程序期望在访问者到来的同一请求中获取提交的数据,则不会执行。从域2的角度来看,执行POST请求的客户端将是托管域1的服务器而不是客户端浏览器,如果客户端IP很重要或在域2上进行其他客户端检查,则这一点非常重要。 如果POST请求用于显示特定于客户端的内容,则还必须进行一些服务器端跟踪,以将先前发布的数据与被重定向的访问者组合。

你说的是这样的: "使用curl(或任何好用的HTTP客户端)接收GET参数,并POST到第二个网站,然后将结果传输给浏览器。这被称为代理,并且在我看来可能是一个不错的解决方案。"嗯,那么我该如何将结果传输给浏览器呢?我已经在使用curl了,但是在重定向方面遇到了困难。提前致谢! - arik
这并不是真正的重定向,而是使用POST方法将浏览器的初始GET请求转发到第二个服务器,然后从第二个服务器获取输出并将其返回给浏览器,例如使用echo。我会更新我的答案,并展示代码给你看。 - Benoit
2
我想要补充的一个方面(10年后)是,如果浏览器通过POST发起请求,而服务器想要重定向,但保持原有方法,它可以发送HTTP状态码307和Location头,提示浏览器使用POST重新提交相同的数据。 - arik

7
你可以像下面这样拼凑一些东西...(我并不是说你应该这样做!):
$res = "<form action='/path/to/new/page' method='POST' id='redirectHack'>
            <input type='hidden' id='postVar1' name='postVar1' value='12345'>
            <input type='hidden' id='postVar2' name='postVar2' value='67890'>
        </form>
        <script>
            document.getElementById('redirectHack').submit()
        </script>";
die($res);

这是一些非学术性的编码,但它运行良好。谢谢。 - TommyDo

1

将数据存储在会话中,然后使用 GET。


1
不幸的是,他说由于域问题无法使用会话。 - Mike B
他可以将会话信息存储在共享数据库中,并通过GET传递会话ID。 - ThiefMaster
我对会话并不是很有经验,但是:你的意思是说,只要知道会话 ID,我就可以访问浏览器中存储的其他会话吗? - arik
@arik 浏览器不会存储会话,因此无法访问任何内容。如果您缺乏经验,可以通过提出真正的问题来大大帮助自己,而不是以“这实际上不是我所做的事情,这也不是我运行的实际代码”为形式提问。询问真正的问题是提问的最佳方式。 - Your Common Sense

0
在 POST 重定向 GET 的情况下(参见 https://en.wikipedia.org/wiki/Post/Redirect/Get),使用会话变量作为传输数据的方法是可以接受的。
<?php
session_start();

// return is the name of a checkbox in the post-redirect-get.php script. 
if(isset($_POST['return'])) {
    // We add some data based on some sort of computation and
    // return it to the calling script
    $_SESSION['action']="This string represents data in this example!";
    header('location: post-redirect-get.php');
}

0

不行,你不能用 POST 来进行头部重定向。你有两个选择:

  1. 如果目标地址接受 POST 和 GET 两种请求,你可以使用 GET 替代。
  2. 我们在极少数情况下会添加一个按钮,以备 JavaScript 被关闭的情况。

以下是一个示例:

<noscript>
<div>
<input type="submit" value="Continue"/>
</div>
</noscript>

如果 JavaScript 被关闭,这将显示一个继续按钮,以便用户可以点击继续。

0

这是可能的。在这种情况下,我会使用cURL:

$url = 'http://domain.com/get-post.php';


foreach($_GET as $key=>$value) { 
  $fields_string .= $key.'='.$value.'&'; 
}
rtrim($fields_string,'&');

//open connection
$ch = curl_init();

//set the url, number of POST vars, POST data
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_POST,count($fields));
curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string);

//execute post
$result = curl_exec($ch);

//close connection
curl_close($ch);

$fields_string = rtrim($fields_string,'&'); - Fedir RYKHTIK
我在代码中看不到$fields变量,但是你使用了count($fields) :) - electroid

0
作为@Charles所指示的示例,这里是一个可用的PHP PayPal购买表单,它具有以下功能:
  1. 使用JavaScript检查输入。如果输入正确,则提交表单,否则显示警告。
  2. 使用PHP检查输入。如果输入正确,则创建重定向标头并删除HTML正文,否则再次显示相同的表单。
请注意:
  1. 在服务器上重新检查输入,因为浏览器的输入可能会被恶意篡改。
  2. 在头部命令之前不能输出任何HTML,否则将被忽略并出现PHP警告。
  3. Javascript只能检查输入是否有效,但没有AJAX,无法在提交之前检查用户想要的服务器内容。因此,这种方法是完全不使用Javascript的过程。
  4. 如果重定向目标(如PayPal)仅处理POST数据,则不需要任何HTML。当然,面向人类的目标需要HTML!
  5. 不幸的是,第4点意味着您无法仅发送子集或甚至完整的其他值到新URL,并且让目标页面处理POST数据在用户的浏览器中打开。您无法通过操作$_POST数组来实现这一点(它似乎只是实际数据的PHP副本)。也许有人知道如何修改真正的POST数据集?
  6. 从第5点开始,就没有机会收集原始表单上的私人信息,并仅通过用户的浏览器将付款信息发送到PayPal或其他地方以获得他们的明确付款批准。这意味着需要使用AJAX通过使用两个表单来完成,一个包含不带按钮的私人信息,另一个表单带有PayPal购买按钮,使用AJAX提交另一个表单,并根据结果提交自己的表单。您可以使用PayPal不使用的字段,但它们仍然会获取信息,我们不知道他们是否正在搜索提交的表单数据。
  7. 与第6点不同,更简单的方法是有3个版本的表单:
    1. 初始版本用于捕获私人数据。
    2. 如果出现问题,则重新显示带有提交数据和不正确数据或后端问题指示的表单。
    3. 如果一切正常,则在页面底部通过javascript自动提交PayPal表单,或者如果没有javascript,则通过按钮手动请求提交。

<?php
 // GET POST VARIABLES, ELSE SET DEFAULTS
 $sAction=(isset($_POST['action'])?$_POST['action']:'');
 $nAmount=(int)(isset($_POST['action'])?$_POST['amount']:0);

 // TEST IF AMOUNT OK
 $bOK=($nAmount>=10);

 /*
 TYPICAL CHECKS:
 1. Fields have valid values, as a backup to the javascript.
 2. Backend can fulfil the request.
    Such as whether the requested stock item or appointment is still available,
    and reserve it for 10-15 minutes while the payment goes through.
 If all OK, you want the new URL page, such as PayPal to open immediately.
 */

 // IF OK
 if($bOK){
  // CHANGE HEADER TO NEW URL
  $sURL='https://www.paypal.com/cgi-bin/webscr';
  header('HTTP/1.1 307 Temporary Redirect');
  header('Location: '.$sURL);
 }
?>
<!DOCTYPE html>
<html>
 <head>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  <title>Sample post redirection</title>
 </head>

 <body>
<?php
 // IF NO ACTION OR NOT OK
 if(($sAction=='')||!$bOK){
?>
  <h1>Sample post redirection</h1>
  <p>Throw money at me:</p>
  <form name="pp" action="<?=$_SERVER['REQUEST_URI']?>" method="post" onsubmit="check_form(this)">
<!--   <input type="hidden" name="amount" value="<?=$nAmount?>" /> -->
   <input type="hidden" name="business" value="paypal.email@yourdomain.com" />
   <input type="hidden" name="cmd" value="_xclick" />
   <input type="hidden" name="lc" value="AU" />
   <input type="hidden" name="item_name" value="Name of service" />
   <input type="hidden" name="item_number" value="service_id" />
   <input type="hidden" name="currency_code" value="AUD" />
   <input type="hidden" name="button_subtype" value="services" />
   <input type="hidden" name="no_note" value="0" />
   <input type="hidden" name="shipping" value="0.00" />
   <input type="hidden" name="on0" value="Private" />
   <input type="hidden" name="os0" value="Xxxx xxxxx xxxxx" />
   <p>Amount $<input id="amount" type="text" name="amount" value="<?=$nAmount?>" /> $10 or more.</p>
   <p><button type="submit" name="action" value="buy">Buy</button></p>
  </form>
  <p>If all is OK, you will be redirected to the PayPal payment page.<br />
  If your browser requires confirmation, click the <cite>OK</cite> button.</p>
  <script>
   // JS AT END OF PAGE TO PREVENT HTML RENDER BLOCKING

   // JS FUNCTION FOR LOCAL CHECKING OF FIELD VALUES
   function check_form(oForm){
    // USE TO DETERMINE IF VALUES CORRECT
    var oAmount=document.getElementById('amount');
    var nAmount=oAmount.value;
    var bOK=true;
    var bOK=(nAmount>=10); // EXAMINE VALUES

    // IF NOT OK
    if(!bOK){
     // INDICATE WHAT'S WRONG, ALERT ETC
     alert('Stingy @$#&. Pay more!!');
     
     // BLOCK FORM SUBMIT ON ALL BROWSERS
     event.preventDefault();
     event.stopPropagation();
     return false;
    }
   }
  </script>
<?php
 }
?>
 </body>
</html>


1
你正在告诉浏览器前往新位置,而不是重定向运行在服务器上的脚本。这就是为什么PHP代码无法更改$ _POST变量的原因 - 它们是由浏览器发送到新页面的,而不是由在您的服务器上运行的PHP代码发送的。 - FKEinternet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接