เครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ · URL ตัวเข้ารหัสและตัวถอดรหัส
WHATWG URL มาตรฐานเทียบกับ RFC 3986: เหตุใดเบราว์เซอร์และไลบรารีจึงไม่เห็นด้วย
· พื้นหลัง
การเข้ารหัส URL มาตรฐาน เครื่องมือสำหรับนักพัฒนา
URL มีคำจำกัดความที่มีชีวิตอยู่สองแบบ และไม่เห็นด้วยโดยตั้งใจ โพสต์นี้จะอธิบายว่าทำไม WHATWG จึงเขียนมาตรฐานของตัวเอง โดยที่ทั้งสองต่างกันในการเข้ารหัสและการแยกวิเคราะห์ และโค้ดใดของคุณที่ตามมา
URL ที่ไลบรารีที่เข้มงวดปฏิเสธและเบราว์เซอร์โหลดอย่างมีความสุข — หนึ่งสตริง สองคำตัดสิน
สตริงที่มีแบ็กสแลชใน JavaScript อาจตีความได้อย่างราบรื่นโดยเบราว์เซอร์ของคุณ โดยเป็นส่วนหนึ่งของเส้นทาง URL สตริงเดียวกันนี้ไปถึงแบ็กเอนด์ของไลบรารี Python และปฏิเสธที่จะแยกวิเคราะห์เนื่องจากไม่อนุญาตให้ใช้แบ็กสแลช หนึ่ง URL สองผลลัพธ์ที่แตกต่างกัน ไม่มีอะไรผิด—พวกเขาปฏิบัติตามมาตรฐานที่แตกต่างกัน มาตรฐาน WHATWG อธิบายว่าเบราว์เซอร์ทำอะไรกับ URL ในชีวิตจริง รวมถึงวิธีจัดการกับอินพุตที่มีรูปแบบไม่ถูกต้อง RFC 3986 กำหนดไวยากรณ์อย่างเป็นทางการที่ URL ควรปฏิบัติตามอย่างเหมาะสม ไลบรารีแบ็กเอนด์จำนวนมากที่สร้างขึ้นบน RFC 3986 บังคับใช้ไวยากรณ์นั้นอย่างเคร่งครัดและปฏิเสธสิ่งอื่นใดที่อยู่ภายนอก
ความแตกต่างนี้มีความสำคัญเมื่อคุณย้ายข้อมูลระหว่างสภาพแวดล้อม เบราว์เซอร์ URL ยอมรับอาจไม่ผ่านการตรวจสอบในเครื่องมือแบ็กเอนด์ การทำความเข้าใจว่าโค้ดของคุณใช้มาตรฐานใดจะช่วยป้องกันการแก้ไขข้อบกพร่องของปัญหาแฝง เนื่องจาก URL ทำงานได้ดีในที่เดียว แต่ล้มเหลวอย่างลึกลับในที่อื่นโดยไม่มีเหตุผลที่ชัดเจน
เหตุใด WHATWG จึงเริ่มต้นใหม่ - อธิบายว่าเบราว์เซอร์ทำอะไรจริงๆ กับอินพุตที่มีรูปแบบไม่ถูกต้อง แทนที่จะเป็นสิ่งที่ถูกต้อง
คณะทำงาน WHATWG ก่อตั้งขึ้นใน 2004 เพื่อสร้างมาตรฐานวิธีที่เบราว์เซอร์จัดการ URL ในทางปฏิบัติ แทนที่จะกำหนดกฎอย่างเป็นทางการที่เข้มงวดมากขึ้นซึ่งเบราว์เซอร์จะไม่ปฏิบัติตาม RFC 2396 อธิบายข้อกำหนดทางไวยากรณ์อย่างเป็นทางการ แต่ในทางปฏิบัติเบราว์เซอร์ไม่เคยปฏิบัติตามข้อกำหนดดังกล่าวอย่างถูกต้องทุกประการ เบราว์เซอร์ในโลกแห่งความเป็นจริงได้พัฒนากฎเกณฑ์ที่ใช้งานได้จริงสำหรับการรองรับช่องว่าง การจัดการอักขระที่ใช้ Escape และการกู้คืนจากการป้อนข้อมูลที่มีรูปแบบไม่ถูกต้องซึ่ง RFC ไม่ได้คาดหวังหรือคาดหวัง
RFC 3986 มาถึงใน 2005 พร้อมด้วยไวยากรณ์ที่เป็นทางการสำหรับ URL ที่มีรูปแบบถูกต้องและข้อกำหนดที่เข้มงวด เบราว์เซอร์ใช้ WHATWG; ไลบรารีแบ็กเอนด์มักจะใช้ RFC 3986
ชุดเข้ารหัสเทียบกับอักขระที่สงวนไว้ — รายการต่อส่วนประกอบของ URL Standard เกี่ยวข้องกับหมวดหมู่ของ RFC 3986 อย่างไร
RFC 3986 แบ่งอักขระออกเป็นสามประเภท: สงวนไว้ ไม่สงวนไว้ และทุกอย่างอื่นๆ ที่ต้องเข้ารหัส อักขระที่สงวนไว้ เช่น โคลอน เครื่องหมายทับ เครื่องหมายคำถาม และแฮช มีความหมายเชิงโครงสร้างใน URL อักขระที่ไม่ได้สงวนไว้ ได้แก่ ตัวอักษร ตัวเลข ยัติภังค์ ขีดล่าง จุด และตัวหนอน สิ่งเหล่านี้ปลอดภัยเสมอ ทุกสิ่งทุกอย่างได้รับการเข้ารหัสเปอร์เซ็นต์เป็นไบต์ มาตรฐานมีกฎที่ชัดเจนอยู่ข้อหนึ่ง: รู้ว่าตัวละครของคุณอยู่ในหมวดหมู่ใด
WHATWG URL มาตรฐานใช้แนวทางแบบอิงคอมโพเนนต์ โดยจะระบุกฎการเข้ารหัสที่แตกต่างกันสำหรับแบบแผน สิทธิ์ เส้นทาง แบบสอบถาม และส่วนแยกกัน แทนที่จะใช้หมวดหมู่ส่วนกลาง เครื่องหมายแอมเพอร์แซนด์อาจถูกเข้ารหัสในพาธแต่ปล่อยไว้เพียงลำพังในสตริงการสืบค้น ช่องว่างจะถูกเข้ารหัสเสมอ แต่การแสดงที่แน่นอนจะแตกต่างกันไปตามบริบท การออกแบบตามองค์ประกอบนี้ตรงกับพฤติกรรมของเบราว์เซอร์ได้ดีกว่ามาก แต่ต้องรู้ว่าส่วนใดของ URL ที่คุณกำลังเข้ารหัส
ความทนทานต่อข้อผิดพลาด: ช่องว่าง แบ็กสแลช และแท็บ — อินพุตการปฏิเสธมาตรฐานหนึ่งรายการและการซ่อมแซมอื่นๆ
ช่องว่างจะต้องกลายเป็น %20 ภายใต้ทั้งสองมาตรฐาน แต่เบราว์เซอร์จะแปลงช่องว่างตามตัวอักษรโดยไม่แจ้งให้ทราบ แบ็กสแลชเป็นสิ่งต้องห้ามในทั้งสองมาตรฐาน แต่เบราว์เซอร์บางตัวถือว่าพวกมันเป็นตัวแยกเส้นทาง ห้ามใช้แท็บ บรรทัดใหม่ และอักขระควบคุม WHATWG ระบุพฤติกรรมของตัวแยกวิเคราะห์แบบผ่อนปรน: แปลงหรือละเว้น
อักขระที่ไม่ใช่ ASCII เช่น é หรือ 中 จะต้องเข้ารหัสเปอร์เซ็นต์โดยใช้การเข้ารหัส UTF-8 RFC 3986 ไม่ได้ระบุขั้นตอนการเข้ารหัสอักขระจริงๆ มันถือว่ามีไบต์อยู่ แต่ไม่ได้บอกว่าจะดึงมันมาจากข้อความได้อย่างไร มาตรฐาน WHATWG ต้องการ UTF-8 อย่างชัดเจน: เปลี่ยนสตริงให้เป็น UTF-8 ไบต์ก่อน จากนั้นจึงเข้ารหัสเปอร์เซ็นต์ มาตรฐานทั้งสองให้ผลลัพธ์การเข้ารหัสที่เหมือนกัน แต่เริ่มต้นจากสมมติฐานพื้นฐานที่แตกต่างกัน และไม่มีความชัดเจนเกี่ยวกับสิ่งเดียวกัน
ตัวอย่างการทำงาน: แยกวิเคราะห์ URL ด้วยแบ็กสแลชและช่องว่างในทั้งสองรุ่น - เปรียบเทียบผลลัพธ์
ใช้สตริงตัวอย่าง "https://example.com/café\ search" เบราว์เซอร์พบแบ็กสแลชและเห็นว่าเป็นอักขระพาธ มันเห็นช่องว่างและเข้ารหัสเป็น %20 สร้างสิ่งที่ต้องการ https://example.com/café%5C%20search. ตัวแยกวิเคราะห์ RFC 3986 ปฏิเสธ URL ทั้งหมดทันที เนื่องจากแบ็กสแลชเป็นสิ่งต้องห้ามและช่องว่างเป็นสิ่งต้องห้าม เบราว์เซอร์ยังคงแยกวิเคราะห์ต่อไป ตัวแยกวิเคราะห์ที่เข้มงวดหยุดทำงานโดยสมบูรณ์ ลองตัวอย่างอื่น: "https://user@example.com:80/path?q=a&b=c". ทั้งสองมาตรฐานระบุข้อมูลผู้ใช้ โฮสต์ พอร์ต เส้นทาง และข้อความค้นหาอย่างชัดเจน พวกเขาเห็นด้วยอย่างสมบูรณ์กับ URL ที่มีโครงสร้างนี้ ความขัดแย้งเกิดขึ้นเฉพาะกับอินพุตที่ผิดปกติหรือผิดรูปแบบเท่านั้น
เปิดตัวเข้ารหัสและถอดรหัส URL และเปรียบเทียบโหมด RFC 3986 กับพฤติกรรมของเบราว์เซอร์ วางสตริงที่มีการเว้นวรรค แบ็กสแลช หรือตัวพิมพ์ขอบอื่นๆ เครื่องมือนี้จะแสดงให้คุณเห็นอย่างชัดเจนว่าแต่ละมาตรฐานแปลงอินพุตเดียวกันแตกต่างกันอย่างไร คุณจะเห็นได้ทันทีว่าอันไหนเข้มงวดกว่าและแต่ละคนทำอะไร
สภาพแวดล้อมของคุณใช้แบบใด — เบราว์เซอร์และโหนดเป็นไปตามมาตรฐาน URL ไลบรารีเซิร์ฟเวอร์จำนวนมากปฏิบัติตาม RFC ซึ่งอธิบายไว้โดยทั่วไป
ในเบราว์เซอร์ JavaScript ใช้ WHATWG URL มาตรฐานเป็นค่าเริ่มต้น URL API นำไปใช้งานทุกประการ Node.js ยังใช้ WHATWG ไลบรารี Python มีแนวโน้มที่จะใช้ RFC 3986; urllib ติดตามมันอย่างใกล้ชิด ไลบรารี Java แตกต่างกันไป java.net.URL มีแนวโน้มไปทาง RFC 3986 กล่อง URL ของ Rust ตามหลัง WHATWG net/url ของ Go ได้รับอิทธิพลจาก WHATWG นี่เป็นรูปแบบทั่วไป ไม่ใช่กฎตายตัว
เมื่อคุณสร้าง URL โดยทางโปรแกรมและ URL เหล่านั้นย้ายไปมาระหว่างเบราว์เซอร์และแบ็กเอนด์ ให้เลือกมาตรฐานหนึ่งมาตรฐานและยึดมั่นในมาตรฐานนั้น ใช้ URL API ของเบราว์เซอร์สำหรับ WHATWG หากไลบรารีแบ็กเอนด์ของคุณเข้มงวดมากขึ้น ก็ไม่ใช่สิ่งที่ขัดแย้งกัน แต่เป็นทางเลือกในการออกแบบ
สิ่งนี้ไม่ครอบคลุมถึง — การแยกวิเคราะห์ชื่อโฮสต์, ตัวอักษร IPv6 และการประมวลผล IDNA
การแยกวิเคราะห์ชื่อโฮสต์เกี่ยวข้องกับ IDNA, punycode และกฎของผู้รับจดทะเบียนที่อยู่นอกเหนือ URL การแยกวิเคราะห์ตัวเองทั้งหมด ที่อยู่ IPv6 รูปแบบพิเศษ เช่น mailto: หรือ data: และส่วนประกอบว่างเป็นหัวข้อที่แยกจากกันที่แตกต่างจากการเข้ารหัสเปอร์เซ็นต์โดยสิ้นเชิง การจำกัดความยาวโดเมนและความถูกต้องของชื่อโฮสต์จะแตกต่างกันไปตามผู้รับจดทะเบียน และไม่เกี่ยวข้องกับการสนทนานี้ ยังไม่รวม: การอ้างอิงแบบสัมพันธ์และกฎการแยกวิเคราะห์เฉพาะโครงการ โพสต์นี้เน้นเฉพาะการเข้ารหัสและการแยกวิเคราะห์ความแตกต่าง
การสนทนานี้มุ่งเน้นไปที่การเข้ารหัสและการแยกวิเคราะห์ความแตกต่างที่ทำให้มาตรฐานเหล่านี้แตกต่าง หากไม่รวมกฎชื่อโฮสต์ กฎ DNS และพฤติกรรมเฉพาะโครงการจะช่วยป้องกันความสับสนเกี่ยวกับกฎการเข้ารหัสเปอร์เซ็นต์
ประเด็นสำคัญ: URL เดียวกันนั้นใช้ได้ในโลกหนึ่งและมีข้อผิดพลาดในอีกโลกหนึ่ง — วิธีที่ตัวเข้ารหัสและตัวถอดรหัส URL ให้การเข้ารหัส RFC 3986 ธรรมดาแก่คุณ เพื่อให้คุณเห็นว่าเบราว์เซอร์ใดที่ทำให้เป็นมาตรฐาน
สตริง URL เดียวกันสามารถใช้ได้ภายใต้มาตรฐานหนึ่ง และไม่ถูกต้องภายใต้อีกมาตรฐานหนึ่ง ทั้งสองถูกต้องภายในเป้าหมายการออกแบบของตัวเอง เมื่อเข้ารหัสส่วนประกอบ URL โดยทางโปรแกรม ให้ใช้เครื่องมือที่เหมาะสมสำหรับสภาพแวดล้อมของคุณ WHATWG อธิบายสิ่งที่เบราว์เซอร์ทำจริง RFC 3986 กำหนดไวยากรณ์ที่เป็นทางการ ตัวเข้ารหัสและตัวถอดรหัส URL จะแสดงกฎ RFC 3986 ควบคู่ไปกับพฤติกรรมของเบราว์เซอร์ เพื่อให้คุณสามารถเห็นความแตกต่างที่แน่นอนและเลือกสิ่งที่เหมาะกับสถานการณ์ของคุณ
ปัญหาจะปรากฏขึ้นบ่อยที่สุดเมื่อ URL ข้ามขอบเขตระหว่างเบราว์เซอร์ถึงแบ็กเอนด์ การทำความเข้าใจความแตกต่างนี้หมายถึงการจัดการกับการข้ามนั้นโดยเจตนามากกว่าโดยไม่ได้ตั้งใจหรือโดยไม่ได้ตั้งใจ