ไทย

เครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ · URL ตัวเข้ารหัสและตัวถอดรหัส

Escape() กับ encodeURIComponent: การเข้ารหัส JavaScript ของ URL พัฒนาขึ้นอย่างไร

· พื้นหลัง

javascript การเข้ารหัส URL ประวัติศาสตร์

ฟังก์ชันการเข้ารหัส JavaScript URL สามรุ่น
ภาพประกอบเวกเตอร์ต้นฉบับ ToolAcre

JavaScript มีฟังก์ชันการเข้ารหัส URL มาสามรุ่นแล้ว และรุ่นที่เก่าแก่ที่สุดยังคงแฝงอยู่ในโค้ดที่ใช้งานจริง โพสต์นี้จะอธิบายสิ่งที่ Escape() ทำผิด ทำไม ES3 เพิ่มฟังก์ชัน URI และทำไมพวกเขาถึงรักษา ! * ' ( )

%u20AC ในบันทึกแบบเดิม — ลายนิ้วมือที่ไม่ผิดเพี้ยนของ Escape() และการถอดรหัสล้มเหลว

ไฟล์ JavaScript แบบเดิมมีการเรียกการเข้ารหัส URL โดยใช้ฟังก์ชัน Escape() ที่เลิกใช้แล้ว เอาต์พุตในไฟล์บันทึกหรือข้อความแสดงข้อผิดพลาดมีลำดับ %u20AC ซึ่งเป็นลายนิ้วมือที่ไม่ผิดเพี้ยนของฟังก์ชัน Escape() ที่เลิกใช้แล้วซึ่งไม่มีใครใช้อีก ลำดับนี้ไม่ตรงกับการเข้ารหัส URL มาตรฐานใดๆ และตัวถอดรหัสที่สร้างขึ้นจากกฎ RFC 3986 หรือ WHATWG จะไม่รู้จักลำดับดังกล่าว ข้อมูลไม่สามารถย้อนกลับผ่านเครื่องมือสมัยใหม่ได้ มันเป็นสัญญาณทั่วไปของโค้ดที่มีมาก่อน ES3 และไม่ได้รับการอัปเดตตั้งแต่ปี 1990

ฟังก์ชัน Escape() ได้รับการออกแบบในยุค Netscape ก่อนที่ JavaScript จะมีกฎการเข้ารหัสมาตรฐานหรือ URL อย่างเป็นทางการ โดยเข้ารหัสอักขระที่ไม่ใช่ ASCII ส่วนใหญ่โดยใช้สัญลักษณ์ %uXXXX ซึ่งเป็นรหัสเลขฐานสิบหกสี่หลักที่ไม่มีใครใช้และไม่มีมาตรฐานกำหนดที่ใด สิ่งนี้สมเหตุสมผลสำหรับการใช้งานครั้งเดียวภายในเบราว์เซอร์ แต่มันทำลายความเข้ากันได้กับมาตรฐาน URL และทำให้ข้อมูลไม่สามารถถอดรหัสที่อื่นได้

Escape() และ unescape(): การออกแบบในยุค Netscape - สมมติฐานแบบละติน-1 สิ่งประดิษฐ์ %uXXXX และเหตุใดจึงไม่ตรงกับมาตรฐานใด ๆ

Escape() และ unescape() ถือว่าอินพุตเป็น Latin-1 (ISO 8859-1) ซึ่งเป็นการเข้ารหัสอักขระที่ลงไว้ล่วงหน้า UTF-8 และ Unicode โดยแปลงอักขระแต่ละตัวเป็นรหัสฐานสิบหก โดยใช้ %XX สำหรับอักขระ Latin-1 บิตสูง และ %uXXXX สำหรับทุกสิ่งที่อยู่นอก Latin-1 อักขระที่ไม่ใช่ภาษาลาติน 1 เช่น อิโมจิ ไม่สามารถแสดงได้เลย ฟังก์ชั่นนี้เรียบง่ายและรวดเร็ว แต่ก็ถือว่าผิดอย่างสิ้นเชิงกับกรณีการใช้งานสมัยใหม่

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

ES3 (1999) เพิ่ม encodeURI และ encodeURIComponent - UTF-8 การเข้ารหัสเปอร์เซ็นต์ที่สอดคล้องกับ RFC 2396

ES3 แนะนำสองฟังก์ชัน: encodeURI และ encodeURIComponent ทั้งสองทำการเข้ารหัส UTF-8 เปอร์เซ็นต์: แปลงอักขระที่ไม่ใช่ ASCII ให้เป็น UTF-8 ไบต์ จากนั้นเขียนแต่ละไบต์เป็น %HH ทั้งสองสอดคล้องกับ RFC 2396 ซึ่งเป็นปัจจุบันในขณะนั้น RFC 3986 มาทีหลังและไม่ได้เปลี่ยนพฤติกรรมการเข้ารหัส ฟังก์ชันเหล่านี้ยังคงเป็นมาตรฐานในปัจจุบันและควรใช้

encodeURI มีไว้สำหรับการเข้ารหัส URI ที่สมบูรณ์; encodeURIComponent มีไว้สำหรับการเข้ารหัสส่วนประกอบภายใน URI เช่นค่าการสืบค้นหรือส่วนของเส้นทาง ความแตกต่างนี้สำคัญอย่างยิ่งและง่ายต่อการเข้าใจผิด encodeURI จะรักษาอักขระโครงสร้างเช่น : / ? # @ = & และ ;. encodeURIComponent เข้ารหัสทั้งหมด ปล่อยให้ปลอดภัยที่จะฝังไว้ภายใน URI ที่ใหญ่กว่า

ทำไม ! * ' ( ) ยังคงไม่มีการเข้ารหัส - อักขระ RFC 2396 'เครื่องหมาย' จะถูกตรึงเป็นภาษาหลังจาก RFC 3986 ย้ายอักขระเหล่านั้น

ทั้งสองฟังก์ชันปล่อยให้อักขระเหล่านี้ไม่มีการเข้ารหัส: ตัวอักษร ตัวเลข ยัติภังค์ (-) ขีดล่าง (_) จุด (.) ตัวหนอน (~) และเครื่องหมายวรรคตอนทั้งห้า ! * ' ( ) เครื่องหมายมาจาก RFC 2396 ซึ่งระบุว่าเป็นอักขระ "เครื่องหมาย" ที่ไม่ได้สงวนไว้ RFC 3986 ออกมาใน 2005 และย้ายห้ารายการนั้นไปอยู่ในหมวดหมู่อื่น แต่ JavaScript ได้แช่แข็ง encodeURI และ encodeURIComponent ใน 1999 แล้ว การเปลี่ยนอักขระที่พวกเขาปล่อยให้อยู่คนเดียวจะทำให้โค้ดที่มีอยู่เสียหาย ดังนั้นพวกเขาจึงยังคงอยู่

การตัดสินใจเก็บเครื่องหมายทั้งห้าดังกล่าวโดยไม่เข้ารหัสสำหรับความเข้ากันได้แบบย้อนหลัง หมายความว่าการเข้ารหัสของ JavaScript ไม่ตรงกับ RFC 3986 หรือมาตรฐาน WHATWG อย่างสมบูรณ์ มันใกล้พอสำหรับการใช้งานจริง และการเปลี่ยนแปลงในตอนนี้มันเป็นไปไม่ได้เลย นี่คือบทเรียนเกี่ยวกับความเสถียร API: เมื่อคุณหยุดพฤติกรรมแล้ว คุณจะไม่สามารถเปลี่ยนแปลงได้แม้ว่ามาตรฐานจะพัฒนาขึ้นก็ตาม

ตัวอย่างการทำงาน: สตริงเดียวกันผ่านทาง Escape, encodeURI และ encodeURIComponent - เปรียบเทียบสามเอาต์พุต

ใช้สตริง "R&D (การวิจัย) = cafe's" รันผ่าน Escape(), encodeURI และ encodeURIComponent Escape() สร้าง "R%26D%20(research)%20%3D%20caf%E9's" โดยผสมวงเล็บและเครื่องหมายอะพอสทรอฟีที่ไม่ได้เข้ารหัสเข้ากับเครื่องหมายและเครื่องหมายที่เข้ารหัสเปอร์เซ็นต์และเท่ากับ encodeURI สร้าง "R&D%20(การวิจัย)%20=%20caf%C3%A9's" โดยปล่อยให้เครื่องหมายแอมเพอร์แซนด์และเครื่องหมายเท่ากับเพียงอย่างเดียวเนื่องจากเป็นโครงสร้าง encodeURIComponent ผลิต "R%26D%20%28research%29%20%3D%20caf%C3%A9%27s" โดยเข้ารหัสทุกอย่างรวมถึงวงเล็บและเครื่องหมายอะพอสทรอฟี

วางสตริงเดียวกันลงในตัวเข้ารหัสและตัวถอดรหัส URL แล้วสลับระหว่าง encodeURI และ encodeURIComponent เพื่อดูความแตกต่าง จากนั้นตรวจสอบว่า Escape() จะสร้างอะไร (คุณสามารถเรียกมันได้ในคอนโซลของเบราว์เซอร์ แม้ว่ามันจะเตือนคุณก็ตาม) คุณจะเห็นได้ทันทีว่าฟังก์ชันทั้งสามนี้ให้ผลลัพธ์ที่แตกต่างกันโดยสิ้นเชิงสามแบบ

การย้ายออกจาก Escape() - จับคู่การโทรเก่ากับฟังก์ชันสมัยใหม่ที่ถูกต้องและจัดการข้อมูล %uXXXX ที่เก็บไว้

รหัสเก่าที่ใช้ Escape() จะต้องได้รับการอัปเดต หากใช้ Escape() เพื่อเข้ารหัสส่วนประกอบ URI ให้แทนที่ด้วย encodeURIComponent หากใช้เพื่อเข้ารหัส URI ที่สมบูรณ์ ให้ใช้ encodeURI สำหรับข้อมูลที่จัดเก็บซึ่งมีลำดับ %uXXXX คุณต้องมีตัวถอดรหัสแบบกำหนดเอง โดยแปลง %uXXXX แต่ละตัวเป็นจุดโค้ด Unicode จากนั้นรวบรวมจุดโค้ดให้เป็นสตริง unescape() ในตัวของ JavaScript จะอ่าน %uXXXX แต่ผลลัพธ์อาจไม่ถูกต้อง UTF-8

หลังจากแทนที่ Escape() แล้ว ให้ทดสอบโค้ดด้วยสตริงที่มีอักขระที่ไม่ใช่ ASCII เครื่องหมายวรรคตอน และอักขระพิเศษ ผลลัพธ์ควรตรงกับสิ่งที่เครื่องมือและมาตรฐานสมัยใหม่คาดหวัง หากโค้ดของคุณเกิดขึ้นก่อน ES3 อย่างมีนัยสำคัญ โค้ดนั้นอาจใช้รูปแบบที่ล้าสมัยอื่นๆ ด้วย การตรวจสอบที่ครอบคลุมนั้นคุ้มค่ากับความพยายาม

สิ่งนี้ไม่ครอบคลุม — URL และ URLSearchParams API ซึ่งครอบคลุมแยกกัน

URL และ URLSearchParams API ที่เพิ่มเข้ามาในภายหลัง มอบอินเทอร์เฟซระดับที่สูงกว่าสำหรับการสร้าง URL และการเข้ารหัสส่วนประกอบ พวกเขาจัดการการหลบหนีทั้งหมดโดยอัตโนมัติและตรงกับมาตรฐาน WHATWG URL ทุกประการ เป็นวิธีที่นิยมใช้ในการสร้าง URL โดยทางโปรแกรมใน JavaScript สมัยใหม่

โพสต์นี้ครอบคลุมเฉพาะฟังก์ชันการเข้ารหัส ไม่ใช่ API ระดับที่สูงกว่า URL และโครงสร้างการแยกวิเคราะห์ URLSearchParams เลือกกฎส่วนประกอบและทำให้ผลลัพธ์เป็นอนุกรม ในขณะที่ encodeURIComponent แปลงสตริงที่ให้มาหนึ่งสตริงโดยไม่รู้ว่าจะวางไว้ที่ไหน ความแตกต่างนั้นอยู่ที่ขอบเขต: ย้ายการเรียกใช้ Escape() แบบเก่าโดยพิจารณาว่าจะจัดการค่าหรือที่อยู่หรือไม่ จากนั้นให้พิจารณาแทนที่การต่อข้อมูลแบบแมนนวลโดยรอบด้วย API ที่มีโครงสร้างเป็นรีแฟกเตอร์แยกต่างหาก

ประเด็นสำคัญ: สามฟังก์ชัน หนึ่งคู่ที่ยังมีชีวิตอยู่ - วิธีที่ตัวเข้ารหัสและตัวถอดรหัส URL แสดงพฤติกรรม encodeURI และ encodeURIComponent สมัยใหม่เคียงข้างกัน

การพัฒนา JavaScript สมัยใหม่ควรใช้ encodeURI หรือ encodeURIComponent ห้ามใช้ Escape() ฟังก์ชันได้รับมาตรฐานใน 1999 และไม่มีการเปลี่ยนแปลงตั้งแต่นั้นมา โดยเข้ารหัสอักขระที่ไม่ใช่ ASCII เป็นไบต์ UTF-8 และจัดการอักขระมาตรฐานที่สงวนไว้อย่างถูกต้อง เครื่องมือตัวเข้ารหัสและตัวถอดรหัส URL ใช้ทั้งสองฟังก์ชัน และช่วยให้คุณเห็นพฤติกรรมของทั้งสองแบบเคียงข้างกัน ทำให้ง่ายต่อการเลือกอันที่เหมาะสมสำหรับส่วนประกอบของคุณ

หากคุณพบลำดับ %u ในบันทึกเก่าหรือข้อมูลที่เก็บไว้ ลำดับเหล่านั้นเป็นเอาต์พุต Escape() และควรถูกย้าย การย้ายข้อมูลจะตรงไปตรงมาเมื่อคุณระบุรูปแบบแล้ว รหัสสมัยใหม่ไม่ควรสร้างมันขึ้นมา