เหตุใด HTTP จึงไม่เปลี่ยนเส้นทาง POST


162

การเปลี่ยนเส้นทาง HTTP ทำได้โดยใช้รหัส HTTP 301 และ 302 (อาจเป็นรหัสอื่น) และฟิลด์ส่วนหัวที่เรียกว่า "ตำแหน่ง" ซึ่งมีที่อยู่ของสถานที่ใหม่ที่จะไป อย่างไรก็ตามเบราว์เซอร์จะส่งคำขอ "GET" ไปยัง URL นั้นเสมอ

อย่างไรก็ตามหลายครั้งคุณต้องเปลี่ยนเส้นทางผู้ใช้ของคุณไปยังโดเมนอื่นผ่านทาง POST (ตัวอย่างเช่นการชำระเงินธนาคาร) นี่เป็นสถานการณ์ทั่วไปและเป็นข้อกำหนดจริงๆ ไม่มีใครรู้ว่าทำไมความต้องการทั่วไปดังกล่าวจึงถูกเพิกเฉยในข้อกำหนด HTTP วิธีแก้ปัญหาคือการส่งแบบฟอร์ม (พร้อมพารามิเตอร์ในเขตข้อมูลที่ซ่อนอยู่) พร้อมชุดการดำเนินการไปยังตำแหน่งเป้าหมาย (ค่าของฟิลด์ส่วนหัวLocation ) และใช้setTimeoutเพื่อส่งแบบฟอร์มไปยังตำแหน่งเป้าหมาย


1
รหัสสถานะ 307 เป็นสิ่งที่คุณกำลังมองหา? ดูคำตอบของฉันด้านล่าง
David Ruttka

คำตอบ:


180

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

ดังที่คนอื่น ๆ พูดกันว่ามีความเป็นไปได้ที่จะนำไปใช้ในทางที่ผิดซึ่งอาจเป็นเหตุผลว่าทำไมกรอบงานจำนวนมากติดอยู่ที่ 301 และ 302 ในนามธรรมของพวกเขา อย่างไรก็ตามด้วยความเข้าใจที่ถูกต้องและการใช้งานที่รับผิดชอบคุณควรจะสามารถบรรลุสิ่งที่คุณต้องการได้

โปรดทราบว่าเป็นไปตามสเปค W3.orgเมื่อMETHODไม่ได้HEADหรือGET, ตัวแทนผู้ใช้ควรแจ้งให้ผู้ใช้ก่อน re-ดำเนินการคำขอที่สถานที่ใหม่ คุณควรให้บันทึกและกลไกทางเลือกสำหรับผู้ใช้ในกรณีตัวแทนผู้ใช้เก่าไม่แน่ใจว่าจะทำอย่างไรกับ 307

ใช้แบบฟอร์มนี้:

<form action="Test307.aspx" method="post">
    <input type="hidden" name="test" value="the test" />
    <input type="submit" value="test" />    
</form>

และการมี Test307.aspx เพียงส่งคืน 307 พร้อมที่ตั้ง: http://google.co.th , Chrome 13 และ Fiddler ยืนยันว่า "test = the test" ถูกส่งไปยัง Google แล้ว แน่นอนว่าการตอบสนองเพิ่มเติมคือ 405 เนื่องจาก Google ไม่อนุญาตให้ใช้ POST แต่มันแสดงให้เห็นกลไก

สำหรับข้อมูลเพิ่มเติมโปรดดูที่รายการของรหัสสถานะ HTTPและข้อมูลจำเพาะ W3.org

307 การเปลี่ยนเส้นทางชั่วคราว (ตั้งแต่ HTTP / 1.1) ในโอกาสนี้คำขอควรทำซ้ำกับ URI อื่น แต่คำขอในอนาคตยังสามารถใช้ URI เดิมได้ 2ตรงกันข้ามกับ 303 วิธีการร้องขอไม่ควรเปลี่ยนแปลงเมื่อออกคำขอเดิมอีกครั้ง ตัวอย่างเช่นคำขอ POST จะต้องทำซ้ำโดยใช้คำขอ POST อื่น


2
@DavidRuttka อะไรการสนับสนุนเบราว์เซอร์ในป่า ?
Pacerier

5
@DavidRuttka คุณอาจต้องการอัปเดตคำตอบของคุณเพื่อพิจารณา rfc7231 เข้าบัญชี (ล้าสมัย rfc2616) การให้ผู้ใช้ขึ้นอยู่กับข้อกำหนดใน rfc2616 ความต้องการนี้ลดลงใน rfc7231 และ rfc7231 ยังแนะนำข้อกำหนดที่ 307 การเปลี่ยนเส้นทางจะต้องไม่เปลี่ยนวิธีการร้องขอ (ซึ่งคุณพูดถึงในตอนท้ายของคำตอบของคุณ)
nibarius

โปรดทราบว่าตามtools.ietf.org/id/draft-hunt-http-rest-redirect-00.html "รหัสการเปลี่ยนเส้นทาง HTTP 301-306 ไม่ควรใช้เว้นแต่ผู้ให้บริการจะทราบว่าลูกค้าเป็นผู้ใช้จริง - เอเจนต์ "ดังนั้นดูเหมือนว่าบริการ ReSTful ควรใช้ 308 แทนที่จะเป็น 301 อย่างไรก็ตามนี่เป็นเพียงร่างจดหมายเท่านั้น
Bruce Adams

49

ผมพบว่าคำอธิบายที่ดีเกี่ยวกับเรื่องนี้หน้านี่

สถานการณ์ที่ง่ายที่สุดใน WWW คือการทำธุรกรรม "idempotent" นั่นคือสถานการณ์ที่สามารถทำซ้ำได้โดยไม่ก่อให้เกิดอันตรายใด ๆ โดยทั่วไปแล้วธุรกรรมเหล่านี้เป็น "GET" เนื่องจากเป็นการดึงการอ้างอิง URL ที่ตรงไปตรงมา (เช่น href = หรือ src = คุณลักษณะใน HTML) หรือเนื่องจากเป็นการส่งแบบฟอร์มโดยใช้วิธีการ GET การเปลี่ยนเส้นทางธุรกรรมประเภทนั้นเป็นเรื่องตรงไปตรงมาและไม่มีคำถามที่ถาม: ลูกค้าได้รับการตอบสนองการเปลี่ยนเส้นทางรวมถึงส่วนหัว Location: ที่ระบุ URL ใหม่และลูกค้าตอบสนองต่อมันด้วยการออกธุรกรรมใหม่ไปยัง URL ใหม่ มีความแตกต่างระหว่างรหัสสถานะ 30 เท่าที่เชื่อมโยงกับการเปลี่ยนเส้นทางเหล่านี้ในความสามารถในการแคชโดยนัย แต่ไม่เช่นนั้นจะคล้ายกัน (301 และ 302) เพื่อตอบสนองต่อคำขอ GET

การทำธุรกรรม POST นั้นแตกต่างกันเนื่องจากโดยหลักแล้วจะไม่ใช่ idempotent (เช่นการสั่งพิซซ่าการลงคะแนนเสียงหรืออะไรก็ตาม) และต้องไม่ทำซ้ำโดยพลการ

ข้อกำหนดคุณสมบัติโปรโตคอล HTTP ได้รับการออกแบบให้คำนึงถึงความแตกต่างนี้: วิธีการ GET ถูกกำหนดให้เป็น idempotent โดยเนื้อแท้ในขณะที่วิธีการ POST ถูกกำหนดให้เป็นอย่างน้อยอาจไม่ใช่ idempotent; ข้อกำหนดเรียกร้องให้มีข้อควรระวังจำนวนมากที่ตัวแทนลูกค้า (เช่นเบราว์เซอร์) เพื่อป้องกันผู้ใช้จากการส่งธุรกรรม POST ที่ไม่ได้ตั้งใจโดยไม่ตั้งใจหรือส่ง POST ไปยังบริบทที่พวกเขาไม่ต้องการ .

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


เหตุผลส่วนใหญ่ไปถึงวันที่ intertubes ช้าและไม่น่าเชื่อถือ (ซึ่งพวกเขายังคงอยู่ในหลายสถานที่ของโลก) ฉันจำได้ชัดเจนเมื่อใช้สายโทรศัพท์และจะตัดการเชื่อมต่อแบบสุ่มทุกครั้งที่มีคนหยิบโทรศัพท์ขึ้นมา เป็นการดีกว่าที่จะโหลดหน้าเว็บซ้ำอีกครั้งและดูว่าสถานะเซิร์ฟเวอร์อยู่ในสถานะใดมากกว่าที่จะส่งสิ่งต่าง ๆ และเรียกใช้ความเสี่ยงในการดำเนินการซ้ำสองครั้ง
zzzzBov

@ เหยี่ยว, การเพิ่ม "ตัวนับผู้เยี่ยมชม" จะถือว่าไม่ใช่ idempotent หรือไม่? ถ้าเป็นเช่นนั้นแทบจะไม่มีเว็บไซต์ใดที่วันนี้จะได้รับ idempotent ...
Pacerier

@Pacerier: โดยทั่วไป idempotent ตีความว่าเป็น "idempotent อย่างมีความหมาย" ตัวอย่างเช่นซื้อไอเท็มเดียวกันสองครั้งไม่ใช่การตอกบัตรสองครั้ง มิฉะนั้นคุณจะค่อนข้างถูกต้อง แต่จริงๆแล้วข้อมูลจำเพาะควรมีเซิร์ฟเวอร์ที่จำเป็นต้องใช้ idempotent อย่างมีความหมายหากจำเป็นเช่นการฝัง ID ในหน้าเพื่อป้องกันการซ้ำซ้อน - ไม่ต้องการให้เบราว์เซอร์ถามคำถามที่พวกเขาไม่มีทางตอบคำถามใด ๆ ได้อย่างแม่นยำ การป้องกันการเปลี่ยนเส้นทางของ POST จะไม่ส่งผลกระทบต่อ idempotency เป็นเพียงข้อความแจ้งว่าเป้าหมายของคำขอนั้นอยู่ที่นั่นจริงๆ
Lawrence Dol

ฉันไม่เห็นว่าสิ่งนี้สมเหตุสมผลสำหรับเหตุผลนี้อย่างไร สมมติว่าฉันอยู่ในเว็บไซต์ Chase bank และฉันส่งแบบฟอร์ม ฉันได้ตกลง / ไว้วางใจพวกเขาแล้ว ดังนั้นหากพวกเขาต้องเปลี่ยนเส้นทางข้อมูลไปยังหน้าอื่นทำไมฉันต้องยอมรับอีกครั้ง หรืออีกตัวอย่างหนึ่งสมมติว่าฉันเป็นคนที่ปิด JavaScript ตามค่าเริ่มต้น วันหนึ่งฉันไปกรอกแอพจำนองออนไลน์และเมื่อฉันส่งแบบฟอร์มมันมีข้อผิดพลาด มันจะดีถ้าแอปสามารถเปลี่ยนเส้นทาง (ด้วย POST) ไปยังหน้าที่ฉันเพิ่งกรอกเพื่อเติมข้อมูลล่วงหน้า
b01

@ Flacon ฉันต้องการหลักฐานว่าการ จำกัด การเปลี่ยนเส้นทางด้วย POST สามารถป้องกันการทำร้ายร่างกายได้ทุกเรื่อง ตั้งแต่แรกที่ฉันต้องเชื่อถือแอพกับข้อมูลของฉันพวกเขาสามารถทำสิ่งที่พวกเขาต้องการได้เมื่อพวกเขามีข้อมูล และฉันไม่คิดว่าการเปลี่ยนเส้นทางมีความเสี่ยงมากกว่าคำขอด้วย POST
b01

3

GET (และวิธีการอื่น ๆ สองสาม) ถูกกำหนดเป็น 'ปลอดภัย' ในข้อมูลจำเพาะ http ( RFC 2616 ):

9.1.1 วิธีการที่ปลอดภัย

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

โดยเฉพาะอย่างยิ่งการประชุมได้รับการจัดตั้งขึ้นว่าวิธีการ GET และหัวไม่ควรมีความสำคัญของการดำเนินการอื่นนอกเหนือจากการดึง วิธีการเหล่านี้ควรได้รับการพิจารณาว่า "ปลอดภัย" สิ่งนี้อนุญาตให้ตัวแทนผู้ใช้แสดงวิธีการอื่น ๆ เช่น POST, PUT และ DELETE ในวิธีพิเศษเพื่อให้ผู้ใช้ทราบถึงความจริงที่ว่ามีการร้องขอการดำเนินการที่ไม่ปลอดภัย

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

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

แม้ว่าสิ่งนี้จะเปลี่ยนไปด้วย JavaScript แต่เดิมมีส่วนต่อประสานผู้ใช้ที่แตกต่างกัน - ผู้ใช้สามารถเรียกใช้คำขอ GET โดยคลิกที่ลิงก์ แต่จะต้องกรอกแบบฟอร์มเพื่อเรียกใช้คำขอ POST ฉันคิดว่าผู้ออกแบบ HTTP มีความกระตือรือร้นที่จะรักษาความแตกต่างระหว่างวิธีการที่ปลอดภัยและไม่ปลอดภัย

ฉันยังไม่คิดว่าจะต้องเปลี่ยนเส้นทางไปยัง POST การดำเนินการใด ๆ ที่ต้องดำเนินการสามารถสันนิษฐานได้โดยการเรียกใช้ฟังก์ชันภายในโค้ดด้านเซิร์ฟเวอร์หรือหากต้องเกิดขึ้นบนเซิร์ฟเวอร์อื่นจากนั้นแทนที่จะส่งการเปลี่ยนเส้นทางที่มี URL สำหรับเบราว์เซอร์ไปยัง POST ไปยังเซิร์ฟเวอร์ สามารถทำการร้องขอไปยังเซิร์ฟเวอร์นั้นโดยทำหน้าที่เหมือนพร็อกซีสำหรับผู้ใช้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.