PHP Redirect กับ POST data


241

ฉันได้ทำการวิจัยในหัวข้อนี้แล้วและมีผู้เชี่ยวชาญบางคนที่บอกว่ามันเป็นไปไม่ได้ดังนั้นฉันจึงต้องการขอทางเลือกอื่น

สถานการณ์ของฉัน:

หน้า A: [checkout.php] ลูกค้ากรอกรายละเอียดการเรียกเก็บเงิน

หน้า B: [process.php] สร้างหมายเลขใบแจ้งหนี้และจัดเก็บรายละเอียดลูกค้าในฐานข้อมูล

หน้า C: [thirdparty.com] เกตเวย์การชำระเงินที่สาม (ยอมรับเฉพาะข้อมูลการโพสต์)

ลูกค้ากรอกรายละเอียดและตั้งค่ารถเข็นของพวกเขาในหน้า A จากนั้นโพสต์ไปที่หน้า B. ภายใน process.php เก็บข้อมูล POSTed ในฐานข้อมูลและสร้างหมายเลขใบแจ้งหนี้ หลังจากนั้นให้โพสต์ข้อมูลลูกค้าและหมายเลขใบแจ้งหนี้ไปยังเกตเวย์การชำระเงิน thirdparty.com ปัญหากำลังดำเนินการ POST ในหน้า B. cURL สามารถ POST ข้อมูลไปยังหน้า C แต่ปัญหาคือหน้าไม่ได้เปลี่ยนเส้นทางไปยังหน้า C ลูกค้าต้องกรอกรายละเอียดบัตรเครดิตในหน้า C

เกตเวย์การชำระเงินของบุคคลที่สามให้ตัวอย่าง API กับเราตัวอย่างคือโพสต์หมายเลขใบแจ้งหนี้พร้อมรายละเอียดลูกค้า เราไม่ต้องการให้ระบบสร้างหมายเลขใบแจ้งหนี้ที่ไม่ต้องการเกินจำนวน

มีวิธีแก้ไขปัญหานี้หรือไม่? โซลูชันปัจจุบันของเราคือให้ลูกค้ากรอกรายละเอียดในหน้า A จากนั้นในหน้า B เราสร้างหน้าอื่นที่แสดงรายละเอียดลูกค้าทั้งหมดที่นั่นโดยผู้ใช้สามารถคลิกปุ่มยืนยันเพื่อ POST ไปที่หน้า C

เป้าหมายของเราคือให้ลูกค้าคลิกเพียงครั้งเดียว

หวังว่าคำถามของฉันชัดเจน :)


ฉันไม่คิดว่าจะซ้ำซ้อนกับสิ่งนี้เป็นเพราะเป้าหมายของฉันอยู่ในหน้า PHP ส่งต่อข้อมูล POST และเปลี่ยนเส้นทาง Curl ฉันไม่คิดว่าเป็นไปได้ที่จะทำมัน เพียงแค่หาผู้เชี่ยวชาญทางเลือกใด ๆ สำหรับมัน
Shiro

redirect รหัสการเปลี่ยนเส้นทาง 308?
user2284570

คำตอบ:


212

สร้างแบบฟอร์มบนหน้า B พร้อมข้อมูลที่จำเป็นทั้งหมดและการดำเนินการที่กำหนดไว้ที่หน้า C และส่งไปพร้อมกับ JavaScript เมื่อโหลดหน้าเว็บ ข้อมูลของคุณจะถูกส่งไปที่หน้า C โดยไม่ต้องยุ่งยากกับผู้ใช้มากนัก

นี่เป็นวิธีเดียวที่จะทำ เปลี่ยนเส้นทางเป็น 303 ส่วนหัว HTTP ที่คุณสามารถอ่านข้อมูลเกี่ยวกับhttp://www.w3.org/Protocols/rfc2616/rfc2616-sec10.htmlแต่ฉันจะพูดบางส่วนของมัน:

การตอบสนองต่อคำขอสามารถพบได้ภายใต้ URI ที่แตกต่างกันและ SHOULD สามารถดึงได้โดยใช้วิธีการ 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/
......

8
คำถามนี่คือสิ่งที่ถ้า Javascript ถูกปิดการใช้งานในลูกค้า? หน้า B จะไม่เปลี่ยนเส้นทางไปยังหน้า C จากนั้นจะเป็นอย่างไร
Imran Omar Bukhsh

24
<noscript><input type="submit" value="Click here if you are not redirected."/></noscript>ข้างใน<form>
ไร้ผล

3
languageแอตทริบิวต์จะเลิกมันควรจะเป็น<script type="text/javascript">...</script>
อเล็กซ์ 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หน้า ที่สำคัญรหัส php ใน$urlหน้านั้นไม่ได้รับการประเมิน
iPadDeveloper2011

@ iPadDeveloper2011 ใน URL คุณไม่มีรหัส PHP! โค้ด PHP ถูกเรียกใช้งานใน SERVER ไม่ใช่ในไคลเอนต์
Eduardo Cuomo

1
redirect_post()ซึ่งเป็นฝั่งเซิร์ฟเวอร์รหัส PHP, $urlส่งคำขอไปยังเซิร์ฟเวอร์สำหรับ หาก$urlเป็น.phpหน้าฉันทราบว่า php ไม่ได้รับการประเมิน - html จะถูกส่งคืนพร้อมแท็ก php ที่ยังอยู่ในนั้น จุดรวมในการส่งข้อมูล POST ไปยังสคริปต์ฝั่งเซิร์ฟเวอร์ไม่ใช่หรือ
iPadDeveloper2011

2
@EduardoCuomo วิธีนี้ใช้ได้กับ URL ของ absoulte เท่านั้นเนื่องจากวิธีการที่ fopen () ทำงาน: php.net/manual/en/function.fopen.php มันยังคงเป็นคำตอบที่ดี
Omn

@Omn คุณถูกต้อง! "สิ่งนี้ใช้ไม่ได้กับ URL ที่เกี่ยวข้อง" ไม่ใช่คำถามเป็นคำสั่ง ขออภัยในความสับสน
Eduardo Cuomo

25

ฉันมีวิธีแก้ไขปัญหาอื่นที่ทำให้เป็นไปได้ มันต้องการให้ลูกค้าใช้จาวาสคริปต์ (ซึ่งฉันคิดว่าเป็นข้อกำหนดที่ยุติธรรมในสมัยนี้)

เพียงใช้คำขอ AJAX ในหน้า A เพื่อไปและสร้างหมายเลขใบแจ้งหนี้และรายละเอียดลูกค้าในพื้นหลัง (หน้า 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
จากนั้นตั้งค่าการดำเนินการ paymentForm ด้วย Javascript หรือไม่ หากปิดการใช้งาน JS แบบฟอร์มจะไม่ส่ง
MikeMurko

ผู้ใช้ปิดการใช้งาน JS ไม่ควรทำให้เรามีปัญหา - เหตุผลที่เราต้องไปที่หน้า B อาจจะสร้างลายนิ้วมือหรือเพิ่มรหัสการสั่งซื้อ ฯลฯ หากไม่มีสิ่งเหล่านี้หน้าบุคคลที่ 3 C ควรล้มเหลวหรือแย่ที่สุด เมื่อผลลัพธ์กลับมาถึงเราจากบุคคลที่สามโดยไม่มีรหัสการสั่งซื้อเราไม่ควรยอมรับคำสั่งซื้อ นี่เป็นภาพรวมที่ดีหาก JS เป็นที่ยอมรับ
Coder

19

$ _SESSION เป็นเพื่อนของคุณถ้าคุณไม่ต้องการยุ่งกับ Javascript

สมมติว่าคุณกำลังพยายามส่งอีเมล:

ในหน้า 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
Adam Baranyai

1
ไม่ฉันหมายถึงไม่เปรียบเทียบกับจาวาสคริปต์ โดยทั่วไปแล้ว $ _SESSION เพียงอย่างเดียวอาจมีความปลอดภัยน้อยกว่าพูดเพียง $ _SESSION + คุกกี้แม้ว่า $ _SESSION จะค่อนข้างปลอดภัยด้วยตัวเอง ข้อมูลเพิ่มเติมที่นี่: stackoverflow.com/questions/17413480/session-spoofing-php
Robert Sinclair

และวิธีปิดเซสชัน
Sayed Muhammad Idrees

คุณต้องการปิดเซสชันหรือไม่?
อีวานพี

6

คุณสามารถปล่อยให้ PHP ทำ POST ได้ แต่ php ของคุณจะได้รับผลตอบแทนพร้อมกับความยุ่งยากทุกประเภท ฉันคิดว่าวิธีที่ง่ายที่สุดคือให้ผู้ใช้ทำ POST

ดังนั้นสิ่งที่คุณแนะนำคุณจะได้รับส่วนนี้:

รายละเอียดการเติมของลูกค้าในหน้า A จากนั้นในหน้า B เราสร้างอีกหน้าหนึ่งแสดงรายละเอียดลูกค้าทั้งหมดที่นั่นคลิกที่ปุ่มยืนยันจากนั้นโพสต์ไปที่หน้าซี

แต่คุณสามารถทำการส่งจาวาสคริปต์ได้ในหน้า B ดังนั้นจึงไม่จำเป็นต้องคลิก ทำให้เป็นหน้า "การเปลี่ยนเส้นทาง" พร้อมภาพเคลื่อนไหวในการโหลดและคุณจะถูกตั้งค่า


นี่คือสิ่งที่เราทำตอนนี้ ฉันคิดว่ามีตรรกะ 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

ต้องใช้จาวาสคริปต์ & jQuery ps สำหรับกรณีนี้ ตามคำแนะนำจากความคิดเห็นของคำตอบอื่น ๆ คุณสามารถใช้ประโยชน์จาก<noscript>แท็กเพื่อสร้างรูปแบบ HTML มาตรฐานในกรณีที่ JS ปิดใช้งาน


5

มีแฮ็คง่ายๆใช้$_SESSIONและสร้างarrayค่าที่โพสต์และเมื่อคุณไปที่File_C.phpคุณสามารถใช้มันแล้วคุณจะดำเนินการหลังจากนั้นทำลายมัน


คุณสามารถให้ตัวอย่างเล็กน้อยของสิ่งที่คุณหมายถึงอะไร
คาโร

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 ซ้ำโดยไม่ได้ตั้งใจ

แต่ถ้าคุณต้องการเปลี่ยนเส้นทางขอโพสต์ด้วยข้อมูล ใน HTTP 1.1 มีรหัสสถานะสำหรับสิ่งนี้ รหัสสถานะ307บ่งชี้ว่าการร้องขอควรทำซ้ำด้วยวิธีและข้อมูล HTTP เดียวกัน ดังนั้นคำขอ POST ของคุณจะถูกทำซ้ำพร้อมกับข้อมูลถ้าคุณใช้รหัสสถานะนี้

SRC


2

ฉันประสบปัญหาคล้ายกันกับคำขอ POST ที่ GET Request ทำงานได้ดีกับแบ็กเอนด์ของฉันซึ่งฉันกำลังส่งตัวแปร ฯลฯ ปัญหาอยู่ที่นั่นแบ็กเอนด์ทำการเปลี่ยนเส้นทางจำนวนมากซึ่งไม่ได้ทำงานกับ 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ข้อมูลลงในสตริงการสืบค้นและส่งต่อไปยังหน้าที่ตามมา แต่ข้อมูลไม่สามารถมีขนาดใหญ่มากเกินไป และนี่อาจไม่ทำงานหากมีการอัปโหลดไฟล์ที่เกี่ยวข้อง แต่ฉันไม่แน่ใจ
กก

0

ลองสิ่งนี้:

ส่งข้อมูลและร้องขอด้วยส่วนหัว http ในหน้า B เพื่อเปลี่ยนเส้นทางไปยังเกตเวย์

<?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

0

ที่นี่มีวิธีการอื่นที่เหมาะกับฉัน:

หากคุณต้องการเปลี่ยนเส้นทางไปยังหน้าเว็บอื่น ( 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'});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.