PHP重定向与POST数据


241

我对此主题进行了一些研究,有些专家说这是不可能的,所以我想寻求替代解决方案。

我的情况:

A页:[checkout.php]客户填写帐单明细。

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

网页C:[thirdparty.com]第三方付款网关(仅接受过帐数据)。

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

第三方支付网关确实给了我们API示例,该示例是将发票编号与客户详细信息一起过帐。我们不希望系统生成多余的发票编号。

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

我们的目标是让客户只需单击一次即可。

希望我的问题清楚:)


我不认为这是重复的,因为我的目标是在PHP页面中,传递POST数据并重定向它。我认为cURL不可行。寻求专家的任何替代选择
Shiro

ʜᴛᴛᴘ308重定向代码?
user2284570

Answers:


212

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

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

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

实现您正在做的事情的唯一方法是使用一个将用户引导到页面C的中间页面。以下是有关如何实现此目标的小片段/简单片段:

<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的用户能够使用您的服务。


2
我的目标是在A页-> C页,B页是控制器(业务逻辑)生成发票编号。从系统视图来看是页面A->页面B->页面C,但是从用户视图来看应该是页面A->页面B,它们永远不应释放存在的页面B。
Shiro

3
@Peeter:聪明的建议+1。唯一的问题是,当第C的推背按钮的用户将被重新提交页C.而且你忘了编码$a$b使用htmlentities/htmlspecialchars,看stackoverflow.com/questions/6180072/php-forward-data-post/...
马可Demaio

8
问题是,如果客户端禁用了Javascript,该怎么办?然后,页面B不会重定向到页面C,是吗?
Imran Omar Bukhsh 2011年

24
<noscript><input type="submit" value="Click here if you are not redirected."/></noscript><form>
空性2013年

3
language属性已弃用,应为<script type="text/javascript">...</script>
Alex W

34
/**
 * Redirect with POST data.
 *
 * @param string $url URL.
 * @param array $post_data POST data. Example: array('foo' => 'var', 'id' => 123)
 * @param array $headers Optional. Extra headers to send.
 */
public function redirect_post($url, array $data, array $headers = null) {
    $params = array(
        'http' => array(
            '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");
    }
}

17
虽然起初看起来也比接受的答案更好,但我注意到它不会重定向到提供的- $url只是将现有页面的内容替换为页面的内容$url重要的是$url页面中的php代码不会被评估。
iPadDeveloper2011 2013年

@ iPadDeveloper2011,在URL中,您没有PHP代码!PHP代码在SERVER中执行,而不是在客户端中执行。
Eduardo Cuomo

1
redirect_post(),这是服务器端的php代码,它向SERVER发送了一个请求$url。如果$url.php页面,请注意,没有对php进行评估-返回的html仍带有php标签。将POST数据发送到服务器端脚本不是很重要吗?
iPadDeveloper2011 2013年

2
@EduardoCuomo由于fopen()的工作原理,该方法仅适用于绝对URL, 但仍是一个很不错的答案。php.net / manual / en / function.fopen.php
Omn

@Omn你是对的!“这不适用于相对URL吗?” 不是问题,是声明。抱歉给您带来混乱
Eduardo Cuomo

25

我有另一个解决方案,使之成为可能。它要求客户端运行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
  }
});

1
此解决方案存在一个问题,如果有人关闭此页面中的JavaScript,刷新并提交,它将进入付款页面而不保存任何内容。这可能是漏洞。
caoglish

2
然后只使用Javascript设置paymentForm操作?如果禁用了JS,则不会提交表单。
MikeMurko 2014年

用户关闭JS不会给我们造成问题-我们必须转到页面B的原因可能是生成指纹或添加订单代码等。如果没有这些,那么第三方页面C将会失败,或者更糟的是,当结果从第三方返回时没有订购代码时,我们不应接受该订购。如果可以接受JS,这总体上是一个很好的解决方案。
编码器

19

如果您不想弄乱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版本安全性低?
亚当·巴兰奈

1
不,我的意思是不比javascript。通常,仅$ _SESSION可能比$ _SESSION + cookie的安全性要差一些,尽管$ _SESSION本身是相当安全的。此处的更多信息:stackoverflow.com/questions/17413480/session-spoofing-php
Robert Sinclair


您需要关闭会议吗?
Ivan P.

6

您可以让PHP执行POST,但随后您的PHP将获得回报,并带有各种复杂性。我认为最简单的方法是实际让用户执行POST。

因此,按照您的建议,您确实会得到以下部分:

在页面A中填写客户详细信息,然后在页面B中创建另一个页面,在此处显示所有客户详细信息,单击确认按钮,然后过帐到页面C。

但是您实际上可以在B页上进行javascript提交,因此无需单击。使它成为带有正在加载动画的“重定向”页面,您已设置好。


这就是我们现在要做的。我在想有没有PHP更好的逻辑来解决它。谢谢。;)
Shiro

是的,更多地用于HTTP,但是我在PHP环境中工作,所以我认为两者都是标记。谢谢!弹片上校。
Shiro

6

我知道这是一个老问题,但是我还有另一个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数据的表单目标页面。

在这种情况下,需要PS JavaScript和jQuery。正如其他答案的注释所建议的那样,如果<noscript>禁用了JS,则可以利用标记创建标准的HTML表单。


5

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


您能否提供一个示例说明您的意思?
caro

3
我的理解是,C页是一个外部资源,它需要发布值,而不是会话。
Narayan Bhandari

3

我知道问题是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数据重定向请求,浏览器将通过GET重定向请求302 redirect这也会删除与该请求关联的所有POST数据。浏览器这样做是为了防止任何意外的重新提交POST事务。

但是,如果您想通过数据重定向POST请求怎么办?在HTTP 1.1中,有一个状态码。状态码307指示应使用相同的HTTP方法和数据重复请求。因此,如果您使用此状态代码,则您的POST请求及其数据将被重复。

SRC


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>';

1

您可以使用会话来保存$_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

0

试试这个:

发送数据并使用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

-1

这是另一种对我有用的方法:

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

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

要么

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

这些替代方法旨在使用GET协议。它在客户端的浏览器上显示变量。OP希望POST具有相同的行为。
Sergio A.

-2
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'});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.