【问题标题】:PHP Redirect with POST data使用 POST 数据进行 PHP 重定向
【发布时间】:2011-07-31 09:39:25
【问题描述】:

我对这个话题做了一些研究,有专家说不是possible,所以想请教一下替代方案。

我的情况:

页面 A:[checkout.php] 客户填写他们的账单详情。

页面 B:[process.php] 生成发票编号并将客户详细信息存储在数据库中。

页面 C:[thirdparty.com] 第三支付网关(仅接受 POST 数据)。

客户填写他们的详细信息并在页面 A 中设置他们的购物车,然后 POST 到页面 B。在 process.php 中,将 POST 数据存储在数据库中并生成发票编号。之后,将客户数据和发票编号发布到 thirdparty.com 支付网关。问题是在页面 B 中进行 POST。cURL 能够将数据 POST 到页面 C,但问题是页面没有重定向到页面 C。客户需要在页面 C 上填写信用卡详细信息。

第三方支付网关确实向我们提供了 API 示例,该示例是 POST 发票编号和客户详细信息。 我们不希望系统生成过多不需要的发票编号。

有什么解决办法吗? 我们目前的解决方案是让客户在页面 A 中填写详细信息,然后在页面 B 中创建另一个页面,显示那里的所有客户详细信息,用户可以在其中单击确认按钮以 POST 到页面 C。

我们的目标是让客户只需点击一次。

希望我的问题很清楚:)

【问题讨论】:

  • 我不认为这是重复的,这是因为我的目标是在 PHP 页面内,传递 POST 数据并重定向它。 cURL 我认为不可能做到这一点。只要寻求专家任何替代它
  • ʜᴛᴛᴘ 308 重定向代码?

标签: php post


【解决方案1】:
/**
  * Redirect with POST data.
  *
  * @param string $url URL.
  * @param array $post_data POST data. Example: ['foo' => 'var', 'id' => 123]
  * @param array $headers Optional. Extra headers to send.
  */
public function redirect_post($url, array $data, array $headers = null) {
  $params = [
    'http' => [
      'method' => 'POST',
      'content' => http_build_query($data)
    ]
  ];

  if (!is_null($headers)) {
    $params['http']['header'] = '';
    foreach ($headers as $k => $v) {
      $params['http']['header'] .= "$k: $v\n";
    }
  }

  $ctx = stream_context_create($params);
  $fp = @fopen($url, 'rb', false, $ctx);

  if ($fp) {
    echo @stream_get_contents($fp);
    die();
  } else {
    // Error
    throw new Exception("Error loading '$url', $php_errormsg");
  }
}

【讨论】:

  • 虽然起初这看起来也比接受的答案更好,但我注意到它不会重定向到提供的$url——它只是用@的内容替换现有页面的内容987654324@ 页。 重要的是$url 页面中的 php 代码不会被评估。
  • @iPadDeveloper2011,在 URL 中,您没有 PHP 代码! PHP 代码在 SERVER 中执行,而不是在客户端中。
  • redirect_post(),这是服务器端的 php 代码,向 SERVER 发送$url 的请求。如果$url.php 页面,我注意到未评估php——返回的html 中仍包含php 标签。将 POST 数据发送到服务器端脚本不是重点吗?
  • @EduardoCuomo 此方法仅适用于绝对网址,因为 fopen() 的工作原理:php.net/manual/en/function.fopen.php 这仍然是一个非常漂亮的答案。
  • @Omn 你是对的! “这不适用于相对 URL?”不是问题,是陈述。很抱歉造成混乱
【解决方案2】:

我在 POST 请求中遇到了类似的问题,其中 GET 请求在我的后端工作正常,我正在传递我的变量等。 问题在于后端做了很多重定向,这不适用于 fopen 或 php 标头方法。

所以我让它工作的唯一方法是放置一个隐藏的表单并在页面加载时使用 POST 提交推送值。

echo
'<body onload="document.redirectform.submit()">   
    <form method="POST" action="http://someurl/todo.php" name="redirectform" style="display:none">
    <input name="var1" value=' . $var1. '>
    <input name="var2" value=' . $var2. '>
    <input name="var3" value=' . $var3. '>
    </form>
</body>';

【讨论】:

  • 嘿@kaya,这看起来很棒,它可以作为原始页面和目标页面之间的中间页面吗?
  • @Daniel,正确 - 我使用它通过 hashValue 从仪表板执行自动登录到网站。仪表板只包含一个没有用户名/密码等用户数据的链接,所以我通过 GET 请求将 hashValue 传递给 LoginHook.php,从那里我从 DB 获取登录信息并通过 POST 请求将用户重定向到目的地。
  • 你是不是忘了加上 input type="text" 或 "hidden" ?
  • 老实说,我不知道,这对我有用,如果你查看 style 属性,整个表单都是隐藏的。
【解决方案3】:

这里还有另一种适合我的方法:

如果您需要重定向到另一个网页 (user.php) 并包含 PHP 变量 ($user[0]):

header('Location:./user.php?u_id='.$user[0]);

header("Location:./user.php?u_id=$user[0]");

【讨论】:

  • 这些替代方案旨在使用 GET 协议。它在客户端的浏览器上公开变量。 OP 希望使用 POST 来实现相同的行为。
【解决方案4】:

如果你不想弄乱 Javascript,$_SESSION 是你的朋友

假设您正在尝试传递电子邮件:

在 A 页上:

// Start the session
session_start();

// Set session variables
$_SESSION["email"] = "awesome@email.com";

header('Location: page_b.php');

在页面 B:

// Start the session
session_start();

// Show me the session!  
echo "<pre>";
print_r($_SESSION);
echo "</pre>";

销毁会话

unset($_SESSION['email']);
session_destroy();

【讨论】:

  • 为什么你认为它不如 javascript 版本安全?
  • 不,我的意思是不与 javascript 相比。总的来说,单独使用 $_SESSION 可能不如说 $_SESSION + cookie 安全,尽管 $_SESSION 本身就相当安全。更多信息:stackoverflow.com/questions/17413480/session-spoofing-php
  • 以及如何关闭会话?
  • 这不是一个有效的选项,因为Page_C 用户想要将信息发布到外部页面,而$_SESSION 将不可用。
  • @Robert Sinclair 非常感谢您的全面回答。真的很有帮助。
【解决方案5】:

我知道问题是面向php,但重定向POST 请求的最佳方式可能是使用.htaccess,即:

RewriteEngine on
RewriteCond %{REQUEST_URI} string_to_match_in_url
RewriteCond %{REQUEST_METHOD} POST
RewriteRule ^(.*)$ https://domain.tld/$1 [L,R=307]

解释:

默认情况下,如果您想使用 POST 数据重定向请求,浏览器会通过带有 302 redirect 的 GET 重定向它。 这也会删除与请求关联的所有 POST 数据。浏览器这样做是为了防止意外重新提交 POST 事务。

但是如果你想用它的数据重定向 POST 请求怎么办?在 HTTP 1.1 中,有一个状态码。 状态码307 表示应使用相同的HTTP 方法和数据重复请求。因此,如果您使用此状态码,您的 POST 请求将与它的数据一起重复。

SRC

【讨论】:

    【解决方案6】:

    试试这个:

    在页面B中发送带有http头的数据和请求以重定向到网关

    <?php
    $host = "www.example.com";
    $path = "/path/to/script.php";
    $data = "data1=value1&data2=value2";
    $data = urlencode($data);
    
    header("POST $path HTTP/1.1\\r\
    " );
    header("Host: $host\\r\
    " );
    header("Content-type: application/x-www-form-urlencoded\\r\
    " );
    header("Content-length: " . strlen($data) . "\\r\
    " );
    header("Connection: close\\r\
    \\r\
    " );
    header($data);
    ?>
    

    附加标题:

    Accept: \*/\*
    User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like 
    Gecko) Chrome/74.0.3729.169 Safari/537.36
    

    【讨论】:

      【解决方案7】:

      您可以使用会话保存$_POST 数据,然后检索该数据并在后续请求中将其设置为$_POST

      用户提交请求到 /dirty-submission-url.php
      做:

      if (session_status()!==PHP_SESSION_ACTIVE)session_start();
           $_SESSION['POST'] = $_POST;
      }
      header("Location: /clean-url");
      exit;
      

      然后浏览器重定向到您的服务器并请求/clean-submission-url。您将有一些内部路由来弄清楚如何处理它。
      在请求开始时,您将执行以下操作:

      if (session_status()!==PHP_SESSION_ACTIVE)session_start();
      if (isset($_SESSION['POST'])){
          $_POST = $_SESSION['POST'];
          unset($_SESSION['POST']);
      }
      

      现在,通过您的其余请求,您可以像在第一次请求时一样访问$_POST

      【讨论】:

      • 您也可以将$_POST 数据处理为查询字符串,然后以这种方式将其传递到后续页面。但是数据不能太大。如果涉及文件上传,这可能不起作用,但我不确定。
      • 你让我很开心谢谢@Reed
      • 这里似乎有点意思。那 $_SESSION['POST'] = $_POST;似乎拥有魔力
      【解决方案8】:

      在页面 B 上生成一个表单,将所有必需的数据和操作设置为页面 C,并在页面加载时使用 JavaScript 提交它。您的数据将被发送到页面 C,而不会给用户带来太多麻烦。

      这是唯一的方法。重定向是一个 303 HTTP 标头,您可以在 http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 上阅读它,但我会引用其中的一些内容:

      对请求的响应可以是 在不同的 URI 下找到并且应该 使用 GET 方法检索 那个资源。这个方法存在 主要是为了允许一个输出 POST 激活脚本重定向 用户代理到选定的资源。这 新 URI 不是替代引用 对于最初请求的资源。 303 响应不能被缓存, 但对第二个的回应 (重定向的)请求可能是 可缓存。

      实现您正在做的事情的唯一方法是使用将用户发送到页面 C 的中间页面。这是一个关于如何实现该目标的小/简单 sn-p:

      <form id="myForm" action="Page_C.php" method="post">
      <?php
          foreach ($_POST as $a => $b) {
              echo '<input type="hidden" name="'.htmlentities($a).'" value="'.htmlentities($b).'">';
          }
      ?>
      </form>
      <script type="text/javascript">
          document.getElementById('myForm').submit();
      </script>
      

      您还应该在 noscript 标记内有一个简单的“确认”表单,以确保没有 Javascript 的用户能够使用您的服务。

      【讨论】:

      • 我的目标是在A页->C页,B页是控制器(业务逻辑)生成发票号。从系统视图是Page A-> Page B-> Page C,但从用户视图应该是Page A->Page B,他们不应该释放Page B存在。
      • @Peeter:聪明的建议 +1。唯一的问题是当页面 C 上的用户按下后退按钮时重新提交到页面 C。此外,您忘记使用 htmlentities/htmlspecialchars$a$b 进行编码,请参阅 stackoverflow.com/questions/6180072/php-forward-data-post/…
      • 这里的问题是,如果客户端禁用了 Javascript 会怎样?那么页面 B 不会重定向到页面 C 是吗?
      • &lt;noscript&gt;&lt;input type="submit" value="Click here if you are not redirected."/&gt;&lt;/noscript&gt;&lt;form&gt;
      • language 属性已弃用,应为 &lt;script type="text/javascript"&gt;...&lt;/script&gt;
      【解决方案9】:

      有一个简单的 hack,使用$_SESSION 并创建一个发布值的array,一旦你转到File_C.php,你就可以使用它,然后在销毁它之后进行处理。

      【讨论】:

      • 你能举一个小例子来说明你的意思吗?
      • 我的理解是,Page C 是一个外部来源,它接受的是 post 值,而不是 session。
      • 会话值在不同域可能无法访问
      【解决方案10】:
      function post(path, params, method) {
          method = method || "post"; // Set method to post by default if not specified.
      
      
      
          var form = document.createElement("form");
          form.setAttribute("method", method);
          form.setAttribute("action", path);
      
          for(var key in params) {
              if(params.hasOwnProperty(key)) {
                  var hiddenField = document.createElement("input");
                  hiddenField.setAttribute("type", "hidden");
                  hiddenField.setAttribute("name", key);
                  hiddenField.setAttribute("value", params[key]);
      
                  form.appendChild(hiddenField);
               }
          }
      
          document.body.appendChild(form);
          form.submit();
      }
      

      例子:

       post('url', {name: 'Johnny Bravo'});
      

      【讨论】:

        【解决方案11】:

        我知道这是一个老问题,但我还有另一个使用 jQuery 的替代解决方案:

        var actionForm = $('<form>', {'action': 'nextpage.php', 'method': 'post'}).append($('<input>', {'name': 'action', 'value': 'delete', 'type': 'hidden'}), $('<input>', {'name': 'id', 'value': 'some_id', 'type': 'hidden'}));
        actionForm.submit();
        

        上面的代码使用jQuery创建了一个表单标签,将隐藏字段附加为post字段,最后提交。该页面将转发到附加了 POST 数据的表单目标页面。

        附言这种情况需要 JavaScript 和 jQuery。正如其他答案的 cmets 所建议的,您可以使用 &lt;noscript&gt; 标签创建标准的 HTML 表单,以防 JS 被禁用。

        【讨论】:

          【解决方案12】:

          我有另一个解决方案可以实现这一点。它要求客户端运行 Javascript(我认为现在这是一个公平的要求)。

          只需在页面 A 上使用 AJAX 请求即可在后台(您之前的页面 B)中生成您的发票编号和客户详细信息,然后在请求成功返回并提供正确信息后 - 只需完成表单提交即可您的支付网关(C 页)。

          这将实现用户只需单击一个按钮并进入支付网关的结果。下面是一些伪代码

          HTML:

          <form id="paymentForm" method="post" action="https://example.com">
            <input type="hidden" id="customInvoiceId" .... />
            <input type="hidden" .... />
          
            <input type="submit" id="submitButton" />
          </form>
          

          JS(为了方便而使用 jQuery,但制作纯 Javascript 很简单):

          $('#submitButton').click(function(e) {
            e.preventDefault(); //This will prevent form from submitting
          
            //Do some stuff like build a list of things being purchased and customer details
          
            $.getJSON('setupOrder.php', {listOfProducts: products, customerDetails: details }, function(data) {
            if (!data.error) {
              $('#paymentForm #customInvoiceID').val(data.id);
              $('#paymentForm').submit();   //Send client to the payment processor
            }
          });
          

          【讨论】:

          • 这个解决方案有一个问题,如果有人在这个页面关闭了JavaScript,刷新,提交,它会进入支付页面而不保存任何东西。这可能是漏洞。
          • 那么只用Javascript设置paymentForm动作?如果 JS 被禁用,表单将不会提交。
          • 用户关闭 JS 应该不会给我们带来问题 - 我们必须去页面 B 的原因可能是生成指纹或添加订单代码等。没有这些,那么第 3 方页面C 应该失败,或者在最坏的情况下,当结果从第三方返回给我们而没有订单代码时,我们不应该接受订单。如果 JS 可以接受,这总体上是一个很好的解决方案。
          【解决方案13】:

          你可以让 PHP 做一个 POST,但是你的 php 会得到回报,有各种各样的并发症。我认为最简单的方法是让用户进行 POST。

          所以,按照你的建议,你确实会得到这部分:

          客户在页面 A 中填写详细信息,然后在页面 B 中我们创建另一个页面,在那里显示所有客户详细信息,单击确认按钮然后 POST 到页面 C。

          但是您实际上可以在页面 B 上进行 javascript 提交,因此无需单击。让它成为一个带有加载动画的“重定向”页面,然后你就设置好了。

          【讨论】:

          • 这就是我们现在所做的。我在想是否有任何 PHP 更好的逻辑来解决它。谢谢。 ;)
          • 是的,更倾向于 HTTP,但我在 PHP 环境中工作,所以我认为两者都是标签。谢谢!上校弹片。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-03
          • 2011-09-30
          • 2016-09-29
          • 1970-01-01
          相关资源
          最近更新 更多