URI.escape และ CGI.escape แตกต่างกันอย่างไร


คำตอบ:


124

มีความแตกต่างเล็ก ๆ แต่จุดสำคัญคือการที่URI.escapeได้รับการเลิกใช้ในรูบี 1.9.2 ... เพื่อให้การใช้งานCGI::escapeหรือERB :: Util.url_encode

มีการอภิปรายยาวในทับทิมหลักสำหรับผู้ที่สนใจซึ่งยังกล่าวถึงWEBrick :: HTTPUtils.escapeและWEBrick :: HTTPUtils.escape_form


11
เพื่อเพิ่มความสับสน - ฉันเพิ่งเห็นความคิดเห็นในstackoverflow.com/questions/4967608/ …ที่มีคนกล่าวว่า cgi escape ใช้ '+' แทน% 20 สำหรับช่องว่างและมันเทียบกับ 'spec' ...
Louis Sayers

18
อีกทางเลือกหนึ่งคือการใช้ERB::Util.url_encodeที่เหมาะสม%20 สำหรับช่องว่าง
riffraff

1
@Ernest: ดู: github.com/ruby/ruby/commit/… (ตอบกลับการอัปเดต)
Marc-André Lafortune

4
ruby-doc.org/stdlib-2.0.0/libdoc/uri/rdoc/URI/Escape.html มีโมดูล URI.escape ในทับทิม 2.0.0 ทำไมถึงถูกเลิก?
user938363

1
@ user938363 หากคุณคลิกแหล่งที่แสดงที่นั่นคุณจะเห็นว่ายังคงถูกทำเครื่องหมายว่าเลิกใช้แล้ว
drewish

229

อะไรคือความแตกต่างระหว่างขวานกับดาบกับอันที่ฉันควรใช้ มันขึ้นอยู่กับสิ่งที่คุณต้องทำ

URI.escapeควรจะเข้ารหัสสตริง (URL) ลงในจึงเรียกว่า " การเข้ารหัสร้อยละ "

CGI::escapeมาจากข้อมูลจำเพาะCGIซึ่งอธิบายถึงวิธีการเข้ารหัส / ถอดรหัสข้อมูลระหว่างเว็บเซิร์ฟเวอร์และแอปพลิเคชัน

ตอนนี้สมมติว่าคุณต้องหลบหนี URI ในแอปของคุณ มันเป็นกรณีการใช้งานที่เฉพาะเจาะจงมากขึ้น สำหรับสิ่งนั้นชุมชน Ruby ใช้มาURI.escapeนานหลายปี ปัญหาที่เกิดขึ้นURI.escapeคือมันไม่สามารถจัดการสเปค RFC-3896

URI.escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog' 
# => "http://google.com/foo?bar=at%23anchor&title=My%20Blog%20&%20Your%20Blog"

URI.escape ถูกทำเครื่องหมายว่าล้าสมัย:

นอกจากนี้ URI.encode ปัจจุบันเป็น gsub ง่าย ๆ แต่ฉันคิดว่ามันควรแบ่ง URI เป็นส่วนประกอบแล้วหนีแต่ละองค์ประกอบและสุดท้ายเข้าร่วม

ดังนั้น URI.encode ปัจจุบันจึงถือว่าเป็นอันตรายและเลิกใช้แล้ว สิ่งนี้จะถูกลบออกหรือเปลี่ยนพฤติกรรมอย่างมาก

การเปลี่ยนในเวลานี้คืออะไร?

ดังที่ฉันได้กล่าวไว้ข้างต้น URI.encode ปัจจุบันผิดระดับสเป็ค ดังนั้นเราจะไม่ให้การเปลี่ยนที่แน่นอน การเปลี่ยนจะแตกต่างกันไปตามกรณีการใช้งาน

https://bugs.ruby-lang.org/issues/4167

น่าเสียดายที่ไม่มีคำเพียงคำเดียวในเอกสารวิธีเดียวที่จะรู้เกี่ยวกับมันคือการตรวจสอบแหล่งที่มาหรือเรียกใช้สคริปต์ด้วยคำเตือนในระดับ verbose ( -wW2) (หรือใช้ google-fu บางอย่าง)

บางคนเสนอให้ใช้CGI::Escapeสำหรับพารามิเตอร์การสืบค้นเนื่องจากคุณไม่สามารถหลีกเลี่ยง URI ทั้งหมด:

CGI::escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog'
# => "http%3A%2F%2Fgoogle.com%2Ffoo%3Fbar%3Dat%23anchor%26title%3DMy+Blog+%26+Your+Blog"

CGI::escapeควรใช้สำหรับพารามิเตอร์การสืบค้นเท่านั้น แต่ผลลัพธ์จะตรงกับข้อมูลจำเพาะอีกครั้ง จริงๆแล้วกรณีใช้งานที่พบบ่อยที่สุดคือการหลีกเลี่ยงข้อมูลในแบบฟอร์มเช่นในขณะที่ส่งapplication/x-www-form-urlencodedคำขอ POST

ยังกล่าวถึงWEBrick::HTTPUtils.escapeการปรับปรุงไม่มาก (อีกครั้งมันเป็นเพียงง่ายgsubซึ่งก็คือ IMO แม้ตัวเลือกที่แย่กว่าURI.escape):

WEBrick::HTTPUtils.escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog'
# => "http://google.com/foo?bar=at%23anchor&title=My%20Blog%20&%20Your%20Blog" 

ที่ใกล้เคียงกับสเปคที่ดูเหมือนว่าจะเป็นแอดเดรสอัญมณี:

require 'addressable/uri'
Addressable::URI.escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog'
# => "http://google.com/foo?bar=at#anchor&title=My%20Blog%20&%20Your%20Blog"

โปรดสังเกตว่าไม่เหมือนกับตัวเลือกก่อนหน้านี้ทั้งหมดแอดเดรสสามารถหลีกเลี่ยงได้#และนี่เป็นพฤติกรรมที่คาดหวัง คุณต้องการเก็บ#แฮชไว้ในพา ธ URI แต่ไม่ใช่ในเคียวรี URI

ปัญหาเดียวที่เหลือคือเราไม่ได้หลีกเลี่ยงพารามิเตอร์การสืบค้นอย่างถูกต้องซึ่งนำเราไปสู่ข้อสรุป: เราไม่ควรใช้วิธีการเดียวสำหรับ URI ทั้งหมดเนื่องจากยังไม่มีวิธีแก้ปัญหาที่สมบูรณ์แบบ (จนถึงตอนนี้) อย่างที่คุณเห็น&ไม่ได้หลบหนีจาก "บล็อกของฉัน & บล็อกของคุณ" เราจำเป็นต้องใช้รูปแบบการหลบหนีที่แตกต่างกันสำหรับการค้นหาพารามิเตอร์ซึ่งผู้ใช้สามารถใส่อักขระที่แตกต่างที่มีความหมายพิเศษใน URL ป้อนการเข้ารหัส URL ควรใช้การเข้ารหัส URL สำหรับทุกค่าการสืบค้น "ที่น่าสงสัย" ซึ่งคล้ายกับที่ERB::Util.url_encodeทำ:

ERB::Util.url_encode "My Blod & Your Blog"
# => "My%20Blod%20%26%20Your%20Blog""

มันเจ๋ง แต่เราต้องการ Addressable แล้ว:

uri = Addressable::URI.parse("http://www.go.com/foo")
# => #<Addressable::URI:0x186feb0 URI:http://www.go.com/foo>
uri.query_values = {title: "My Blog & Your Blog"}
uri.normalize.to_s
# => "http://www.go.com/foo?title=My%20Blog%20%26%20Your%20Blog"

สรุป:

  • อย่าใช้URI.escapeหรือคล้ายกัน
  • ใช้CGI::escapeถ้าคุณต้องการหลบหนีจากฟอร์ม
  • หากคุณต้องการทำงานกับ URIs ให้ใช้ Addressable ซึ่งมีการเข้ารหัส URL, การเข้ารหัสฟอร์มและทำให้ URL เป็นปกติ
  • หากเป็นโครงการ Rails ให้ดูที่ " ฉันจะใช้ URL เป็นสตริงใน Rails ได้อย่างไร "

ขอบคุณมากสำหรับข้อมูล แน่ใจว่าได้กำจัดคำเตือนในการทดสอบจอบแล้ว คราดและจอบมองออกไปด้านล่าง
Douglas G. Allen

คำอธิบายที่ยอดเยี่ยม @Ernest แต่ปัญหาของเรื่องนี้คือมันจะไม่ทำงานกับ URL ภายนอกที่ฉันไม่ได้พยายามสร้าง (และไม่สามารถควบคุมได้) เช่นโปรแกรมรวบรวมข้อมูลที่อ่าน URL จากหน้าเว็บจากนั้นพยายามเข้าถึง URL เหล่านั้น (ซึ่งจำเป็นต้องเข้ารหัสก่อนเข้าถึง)
amit_saxena

@amit_saxena หากคุณสามารถAddressableเป็นหนึ่งในอัญมณีของคุณได้คุณสามารถแยกวิเคราะห์ URL เป็นครั้งแรกโดยป้อน rubydoc.info/gems/addressable/Addressable/URI.heuristic_parse
เออร์เนสต์

! ที่น่าสนใจ แต่อีกครั้งฉันไม่สามารถรับแฮชของพารามิเตอร์จาก url ดั้งเดิมที่ใช้สิ่งนี้ซึ่งฉันเข้ารหัสตามที่คุณอธิบาย การไหลในกรณีของฉันคือฉันได้รับ URL ภายนอกจากฟีด -> ซึ่งฉันต้องเข้ารหัส -> ส่งผ่านไปยังไคลเอนต์ HTTP เพื่อดึงเนื้อหา ตอนนี้ถ้าฉันไม่เข้ารหัส URL ภายนอกอย่างถูกต้องไคลเอนต์ HTTP ตาม ruby ​​ล้มเหลวด้วยข้อผิดพลาด URI ที่ไม่ถูกต้อง
amit_saxena

@amit_saxena วิธีแยกวิเคราะห์จะส่งคืนอินสแตนซ์ของจากAddressable:URLนั้นคุณสามารถเรียกใช้วิธีการอินสแตนซ์ทั้งหมดบนมันบางทีหนึ่งในนั้นจะให้ผลลัพธ์ที่คุณต้องการ: rubydoc.info/gems/addressable/Addressable/URI
Ernest


6

CGI::escapeเป็นสิ่งที่ดีสำหรับการหลบหนีส่วนข้อความเพื่อให้สามารถใช้ในพารามิเตอร์การค้นหา URL (สตริงหลังจาก '?') ตัวอย่างเช่นถ้าคุณต้องการมีพารามิเตอร์ที่มีอักขระสแลชใน url คุณ CGI :: escape สตริงนั้นก่อนแล้วจึงใส่เข้าไปใน url

อย่างไรก็ตามใน Rails คุณอาจไม่ได้ใช้งานโดยตรง โดยปกติแล้วคุณจะใช้hash.to_paramซึ่งจะใช้CGI::escapeภายใต้ประทุน


URI::escapeเป็นสิ่งที่ดีสำหรับการหลบหนี URL ซึ่งไม่ได้หลบหนีอย่างถูกต้อง ตัวอย่างเช่นบางเว็บไซต์แสดง URL ที่ไม่ถูกต้อง / ไม่ใช้ Escape ในแท็ก anchor หากโปรแกรมของคุณใช้ URL เหล่านี้เพื่อดึงทรัพยากรเพิ่มเติม OpenURI จะบ่นว่า URL นั้นไม่ถูกต้อง คุณต้องทำURI::escapeสิ่งเหล่านี้เพื่อให้เป็น URL ที่ถูกต้อง ดังนั้นจึงใช้เพื่อหลีกเลี่ยงสตริง URI ทั้งหมดเพื่อให้เหมาะสม ในคำพูดของฉัน URI :: unescape ทำให้ url สามารถอ่านได้โดยมนุษย์และ URI :: escape ทำให้ใช้ได้กับเบราว์เซอร์

นี่เป็นคำศัพท์ธรรมดาของผมและรู้สึกอิสระที่จะแก้ไขให้ถูกต้อง


1

ข้อแตกต่างคือ URI.escape ไม่ทำงาน ...

CGI.escape"/en/test?asd=qwe"
=> "%2Fen%2Ftest%3Fasd%3Dqwe"

URI.escape"/en/test?asd=qwe"
=> "/en/test?asd=qwe"

2
คุณเลือกกรณีทดสอบผิด .. / s,? 's และ =' s เป็นส่วนหนึ่งของ URI ที่ถูกต้องและไม่ได้หลบหนี อักขระอื่น ๆ ที่จำเป็นต้องหลีกเลี่ยงโดยเฉพาะในสตริงแบบสอบถามควรเป็น
เจอราร์ด

@ GerardONeill ฉันเลือกกรณีทดสอบอย่างแม่นยำเพื่อแสดงว่า URI.escape ไม่ทำงานและไม่น่าเชื่อถือ คุณกำลังแนะนำว่า URI.escape กำลังหนีจากสตริงข้อความค้นหาเท่านั้น? จะทราบได้อย่างไรว่าค่าพารามิเตอร์เสร็จสิ้นเมื่อใดหากฉันต้องการเข้ารหัส & ใน บางทีนั่นอาจเป็นสาเหตุที่ทำให้ล้าสมัย?
Radu Simionescu

1
นั่นคือว่าสิ่งที่ฉันพูด URI escape ต้องแยกวิเคราะห์ URL แยกสิ่งที่คิดว่าเป็นพารามิเตอร์แต่ละตัวหนีออกมาและนำกลับมารวมกัน แม้แต่เรื่องนั้นก็อาจจะยุ่ง แต่มันไม่ได้ทำอย่างนั้น - มันแค่หลีกเลี่ยงการหลบหนีตัวละครบางตัวในขณะที่หลบหนีไปส่วนที่เหลือ มันสามารถใช้สำหรับกรณีง่าย ๆ โดยเฉพาะถ้าคุณรู้ว่าพารามิเตอร์ของคุณจะไม่สับสน ..
เจอราร์ดโอนีล

0

CGI.escape ใช้เพื่อหลีกเลี่ยงค่า URL ในสตริงการสืบค้น ตัวละครทั้งหมดที่ไม่ตกอยู่ใน ALPHA, DIGIT, '_', '-', '.' และ '' ชุดอักขระถูกหลีกหนี

แต่นั่นจะทำให้ URL ไม่ถูกต้องเนื่องจาก URL ต้องมี '/', ':', '?', '[', '&', '=', และ ';' อาจจะมากกว่านั้นที่ฉันไม่สามารถคิดถึงหัวของฉันได้

URI.escape ปล่อยให้อักขระ URL เหล่านั้นอยู่คนเดียวและพยายามค้นหาคีย์สตริงการสืบค้นและค่าที่จะหลบหนี อย่างไรก็ตามสิ่งนี้ไม่สามารถพึ่งพาได้จริง ๆ เนื่องจากค่าสามารถมีอักขระทุกชนิดที่ป้องกันการหลบหนีได้ง่าย โดยทั่วไปมันสายเกินไป แต่ถ้า URL สามารถขึ้นอยู่กับว่าง่าย (ไม่มี '&' s และ '=' s ฯลฯ ในค่า) ฟังก์ชั่นนี้อาจถูกใช้เพื่อหลบหนีบางทีตัวอักษรที่อ่านไม่ได้หรือผิดกฎหมาย

โดยทั่วไป - ใช้ CGI.escape บนคีย์และค่าของแต่ละบุคคลก่อนที่จะเข้าร่วมพวกเขาด้วย '&' และเพิ่มพวกเขาหลังจาก '?'


0

CGI.escape ไม่ทำงานกับ OpenProject API มันเข้ารหัส [], และไม่ใช่เครื่องหมาย + ฉันแฮ็คสิ่งนี้ด้วยกันซึ่งดูเหมือนจะใช้งานได้จนถึง API ของ OpenProject แต่ฉันแน่ใจว่ามันหายไป อาจเป็นไปได้ว่าไม่ดีเท่า URI.escape แต่จะไม่ทำให้เกิดข้อผิดพลาดที่ล้าสมัย

class XXX
      def self.encode(path)
        path, query = path.split("?", 2)
        return path if query.nil?
        query = CGI.escape(query).gsub("%3A", ":").gsub("%3D","=").gsub("%5B","[").gsub("%5D","]").gsub("%2C",",").gsub("+","%20")
        return [path,query].join("?")
      end
end

XXX.encode("http://test.com/some/path?query=[box: \"cart\"]")
URI.encode("http://test.com/some/path?query=[box: \"cart\"]")

เอาต์พุตทั้งสอง:

=> " http://test.com/some/path?query=urlbox:%20%22cart%22] "
=> " http://test.com/some/path?query=urlbox:%20 % 22cart% 22] "

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