带有 Post 参数的 PHP 重定向

Posted

技术标签:

【中文标题】带有 Post 参数的 PHP 重定向【英文标题】:PHP Redirection with Post Parameters 【发布时间】:2011-02-21 08:36:22 【问题描述】:

我有一个网页。该网页将用户重定向到另一个网页,或多或少采用以下方式:

<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 隐藏它吗?或者它可以放在&lt;noscript /&gt; 块中。不是很好,但它可能会解决脚本不起作用的问题 使用 POST 数据重定向:***.com/questions/5576619/php-redirect-with-post-data 【参考方案1】:

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

https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.8

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

    header('HTTP/1.1 307 Temporary Redirect');
    header('Location: anotherpage.php');

但是,请注意,根据 HTTP 规范,用户代理必须询问用户是否可以将 POST 信息重新提交到新 URL。实际上,Chrome 不会询问,Safari 也不会,但 Firefox 会向用户显示一个确认重定向的弹出框。根据您的操作限制,也许这没问题,但在一般使用情况下,它肯定有可能给最终用户造成混淆。

【讨论】:

【参考方案2】:

不可能直接从服务器执行此操作,因为 POST 数据应该由浏览器发送。

但你可以选择一个替代方案:

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

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('Location: http://www.domain2.com');

当然,这是带有硬编码值的演示代码,还有 2 点留给您:

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

【讨论】:

您这样说:“接收 GET 参数并使用 curl(或任何像样的 http 客户端)将它们发布到第二个站点,然后将结果传输到浏览器。这称为代理,恕我直言,这可能是一个很好的解决方案。”那么,我怎样才能将结果传输到浏览器呢?我已经在使用 curl,但我很难重定向。提前致谢! 这不是真正的重定向,它更多的是使用 POST 方法将浏览器的初始 GET 请求转发到第二个服务器,然后从第二个服务器获取输出并将其返回给浏览器,例如使用 echo。我将用代码更新我的答案以向您展示 我想补充的一个方面(10 年后)是,如果浏览器通过 POST 发起请求,并且服务器想要重定向,但维护该方法,它可以发送 HTTP 状态码307 旁边的 Location 标头,提示浏览器使用 POST 重新提交相同的数据。【参考方案3】:

可以像下面这样一起破解一些东西......(我不是说你应该但是!):

$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);

【讨论】:

这是一种非学术性的编码,但效果很好。谢谢。【参考方案4】:

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

【讨论】:

不幸的是,他说他不能使用域问题的会话 b/c。 他可以将会话信息存储在共享数据库中,并通过 GET 传递会话 ID。 我对会话不是很有经验,但是:您的意思是我可以通过知道会话 ID 来访问存储在浏览器中的其他会话? @arik 浏览器不存储会话。所以,没有什么可以访问的。您可以通过以“这实际上不是我所做的,这不是我运行的实际代码”的形式提出问题不是,从而极大地帮助自己。由于缺乏经验,最好的提问方式是问真实的问题。【参考方案5】:

没有。您不能使用 POST 进行标头重定向。你有两个选择,

    如果目标接受 POST 或 GET,您可以改用 GET。 我们会在 JavaScript 关闭的极少数情况下添加一个按钮。

这是一个例子,

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

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

【讨论】:

【参考方案6】:

这是可能的。在这种情况下,我会使用 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,'&'); 我在代码中看不到 $fields 变量。但是你使用 count($fields) :)【参考方案7】:

作为@Charles 指示的示例,这是一个有效的 php PayPal 购买表单:

    使用 javascript 检查输入。如果 OK,它会提交它,否则会显示一个警报。 用 php 检查输入。如果正常,它会创建重定向标头并删除正文 HTML,否则它会再次显示相同的表单。

注意:

    应在服务器上重新检查输入,因为浏览器的输入可能会被恶意操纵。 在标头命令之前不能输出 HTML,因为它们将被忽略并出现 php 警告。 Javascript 只能检查输入的有效值,但如果没有 AJAX,将无法在提交之前检查服务器以获取用户想要的任何内容。因此,此方法是完整的非javascript过程。 如果重定向目标(如 PayPal)只处理 POST 数据,则不需要 HTML。人类的目标当然可以! 不幸的是,4 意味着您不能只向新 url 发送一个子集甚至一组完整的其他值,并且在 用户 的浏览器中打开处理 POST 数据的目标页面.您不能通过操作 $_POST 数组来做到这一点(它似乎只是实际数据的 PHP 副本)。或许有人知道如何修改真实的 POST 数据集? 从 5 开始,没有机会收集关于 原始表格,只需发送付款信息 通过用户的浏览器向 PayPal 或其他任何人发送表格,以获得明确的付款批准。这意味着需要 AJAX 来做到这一点 使用两种形式,一种是没有按钮的私人信息,另一种是 另一个带有 PayPal 购买按钮的表单,使用 AJAX 提交 另一种形式,并根据结果,提交自己的形式。 您可以使用 PayPal 不使用的字段,但它们仍然是 获取信息,我们不知道他们在搜寻什么 提交的表单数据。 比起在 6 中使用 AJAX,拥有 3 个版本的表单会简单得多:
      初始捕获私有数据。 如果有问题,请重新显示提交数据的表单并指出不正确的数据或后端问题。 如果正常,一个 PayPal 表单,由 javascript 自动提交至 页面底部 (form.submit()),或者如果没有 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>

【讨论】:

您正在做的是告诉 浏览器 转到新位置,而不是重定向在您的服务器上运行的脚本。这就是 PHP 代码无法更改 $_POST 变量的原因 - 它们是由 浏览器 发送到新页面,而不是由服务器上运行的 PHP 代码发送的。【参考方案8】:

在 POST Redirect 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');

【讨论】:

以上是关于带有 Post 参数的 PHP 重定向的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 post 参数重定向到外部 URL?

如何从 IIS 重定向带有参数的 URL

PHP header()使用POST变量重定向[重复]

使用 post 参数重定向到付款 URL

如何使用 htaccess 将带有参数的旧 url 重定向到新的 seo url

旧到新的 301 重定向参数以实现更好的 Google 迁移?