Apa perbedaan antara URI.escape dan CGI.escape?

147

Apa perbedaan antara URI.escapedan CGI.escapedan mana yang harus saya gunakan?

Tom Lehman
sumber

Jawaban:

124

Ada beberapa perbedaan kecil, tetapi poin pentingnya adalah yang URI.escapesudah usang di Ruby 1.9.2 ... jadi gunakan CGI::escapeatau ERB :: Util.url_encode .

Ada diskusi panjang tentang ruby-core untuk mereka yang tertarik yang juga menyebutkan WEBrick :: HTTPUtils.escape dan WEBrick :: HTTPUtils.escape_form .

Marc-André Lafortune
sumber
11
Hanya untuk menambah kebingungan - Saya baru saja melihat komentar di stackoverflow.com/questions/4967608/... di mana seseorang menyebutkan bahwa cgi escape menggunakan '+' alih-alih% 20 untuk spasi, dan itu bertentangan dengan 'spek' ...
Louis Sayers
18
alternatif menggunakan ERB::Util.url_encodeyang benar digunakan %20 untuk ruang
riffraff
1
@Ernest: Lihat: github.com/ruby/ruby/commit/… (jawaban diperbarui)
Marc-André Lafortune
4
ruby-doc.org/stdlib-2.0.0/libdoc/uri/rdoc/URI/Escape.html . Ada modul URI.escape di ruby ​​2.0.0. Mengapa itu ditinggalkan?
user938363
1
@ user938363 jika Anda mengklik sumber acara di sana Anda akan melihat itu masih ditandai sebagai usang.
drewish
229

Apa perbedaan antara kapak dan pedang dan yang mana yang harus saya gunakan? Yah itu tergantung pada apa yang perlu Anda lakukan.

URI.escapeseharusnya menyandikan string (URL) ke, yang disebut, " Persen-encoding ".

CGI::escapedatang dari spec CGI , yang menjelaskan bagaimana data harus dikodekan / decode antara server web dan aplikasi.

Sekarang, katakanlah Anda harus keluar dari URI di aplikasi Anda. Ini adalah kasus penggunaan yang lebih spesifik. Untuk itu, komunitas Ruby digunakan URI.escapeselama bertahun-tahun. Masalahnya URI.escapeadalah tidak bisa menangani spesifikasi 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 ditandai sebagai usang:

Terlebih lagi URI.encode saat ini adalah gsub sederhana. Tetapi saya pikir itu harus membagi URI ke komponen, kemudian melarikan diri masing-masing komponen, dan akhirnya bergabung dengan mereka.

Jadi URI.encode saat ini dianggap berbahaya dan tidak digunakan lagi. Ini akan dihapus atau mengubah perilaku secara drastis.

Apa penggantinya saat ini?

Seperti yang saya katakan di atas, URI.encode saat ini salah pada tingkat spesifikasi. Jadi kami tidak akan memberikan penggantian yang tepat. Penggantian akan bervariasi sesuai dengan kasus penggunaannya.

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

Sayangnya tidak ada satu kata pun tentang hal itu di dokumen, satu-satunya cara untuk mengetahuinya adalah dengan memeriksa sumbernya, atau menjalankan skrip dengan peringatan di tingkat verbose ( -wW2) (atau menggunakan beberapa google-fu).

Beberapa mengusulkan untuk menggunakan CGI::Escapeparameter kueri, karena Anda tidak dapat menghindari seluruh 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::escapeharus digunakan hanya untuk parameter kueri, tetapi hasilnya akan, sekali lagi, berlawanan dengan spesifikasi. Sebenarnya use-case yang paling umum adalah melarikan diri data formulir, seperti saat mengirim application/x-www-form-urlencodedpermintaan POST.

Disebutkan WEBrick::HTTPUtils.escapejuga tidak banyak perbaikan (sekali lagi itu hanya sederhana gsub, yaitu, IMO, bahkan pilihan yang lebih buruk daripada 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" 

Yang paling dekat dengan spec tampaknya adalah permata yang dapat dialamatkan :

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"

Perhatikan, bahwa tidak seperti semua opsi sebelumnya, Addressable tidak luput #, dan ini adalah perilaku yang diharapkan. Anda ingin menyimpan #hash di jalur URI tetapi tidak di kueri URI.

Satu-satunya masalah yang tersisa adalah bahwa kami tidak lolos parameter kueri kami dengan benar, yang membawa kami pada kesimpulan: kita tidak boleh menggunakan metode tunggal untuk seluruh URI, karena tidak ada solusi yang sempurna (sejauh ini). Seperti yang Anda lihat &tidak luput dari "Blog Saya & Blog Anda". Kita perlu menggunakan bentuk pelolosan yang berbeda untuk permintaan, di mana pengguna dapat menempatkan karakter berbeda yang memiliki arti khusus dalam URL. Masukkan penyandian URL. Pengkodean URL harus digunakan untuk setiap nilai permintaan "mencurigakan", mirip dengan apa yang ERB::Util.url_encodedilakukan:

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

Ini keren tapi kami sudah meminta 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"

Kesimpulan:

  • Jangan gunakan URI.escapeatau serupa
  • Gunakan CGI::escapejika Anda hanya perlu pelarian bentuk
  • Jika Anda perlu bekerja dengan URI, gunakan Addressable, ia menawarkan pengodean URL, pengodean formulir, dan normalisasi URL.
  • Jika ini adalah proyek Rails, lihat " Bagaimana cara saya melepaskan URL dari string di Rails? "
Ernest
sumber
Terima kasih banyak untuk informasinya. Itu pasti menyingkirkan beberapa peringatan pengujian cangkul. Garu dan cangkul terlihat di bawah.
Douglas G. Allen
Penjelasan hebat @Ernest, tetapi masalahnya adalah ini tidak akan berfungsi untuk URL eksternal yang tidak saya coba buat (dan tidak memiliki kendali atas). misal crawler yang membaca URL dari halaman web, dan kemudian mencoba mengakses URL tersebut (yang perlu disandikan sebelum akses).
amit_saxena
@amit_saxena jika Anda mampu memiliki Addressablesalah satu permata Anda, Anda dapat mengurai URL terlebih dahulu, fi rubydoc.info/gems/addressable/Addressable/URI.heuristic_parse
Ernest
Menarik! Tetapi sekali lagi, saya tidak bisa mendapatkan hash parameter dari url asli menggunakan ini, yang kemudian saya encode seperti yang Anda gambarkan. Alur dalam kasus saya adalah: Saya mendapatkan url eksternal dari beberapa umpan -> yang kemudian harus saya encode -> Lewati ke klien http untuk mengambil konten. Sekarang jika saya tidak menyandikan url eksternal dengan benar, klien HTTP berbasis ruby ​​gagal dengan kesalahan URI yang tidak valid.
amit_saxena
Metode parse @amit_saxena akan mengembalikan instance Addressable:URL, Anda kemudian dapat memanggil semua metode instance di atasnya, mungkin salah satu dari mereka akan memberi Anda hasil yang diinginkan: rubydoc.info/gems/addressable/Addressable/URI
Ernest
6

CGI::escapebagus untuk keluar dari segmen teks sehingga mereka dapat digunakan dalam parameter kueri url (string setelah '?'). Misalnya jika Anda ingin memiliki parameter yang mengandung karakter slash di url, Anda CGI :: keluar dari string itu terlebih dahulu dan kemudian masukkan dalam url.

Namun di Rails Anda mungkin tidak akan menggunakannya secara langsung. Biasanya Anda gunakan hash.to_param, yang akan digunakan di CGI::escapebawah tenda.


URI::escapebagus untuk keluar dari url yang tidak lolos dengan benar. Misalnya beberapa situs web menampilkan url yang salah / tidak terhapus di tag jangkar mereka. Jika program Anda menggunakan url ini untuk mengambil lebih banyak sumber daya, OpenURI akan mengeluh bahwa url tidak valid. Anda perlu URI::escapeini untuk menjadikannya url yang valid. Jadi itu digunakan untuk melarikan diri dari seluruh string URI untuk membuatnya lebih baik Dalam kata-kata saya URI :: unescape membuat url dapat dibaca oleh manusia, dan URI :: escape membuatnya valid untuk browser.

Ini adalah istilah awam saya dan merasa bebas untuk memperbaikinya.

lalalala
sumber
1

Perbedaannya adalah bahwa URI.escape tidak berfungsi ...

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

URI.escape"/en/test?asd=qwe"
=> "/en/test?asd=qwe"
Radu Simionescu
sumber
2
Anda memilih test case yang salah .. /'s, ?'s dan = 's semua adalah bagian dari URI yang valid dan dengan demikian tidak lolos. Karakter lain yang harus diloloskan terutama dalam string kueri seharusnya.
Gerard ONeill
@ GerardONeill Saya memilih test case untuk menunjukkan bagaimana URI.escape tidak berfungsi dan tidak dapat diandalkan. Apakah Anda menyarankan agar URI.escape hanya melarikan diri dari string kueri? bagaimana bisa mengetahui kapan nilai parameter selesai jika saya ingin menyandikan & di sana? mungkin itu sebabnya itu usang?
Radu Simionescu
1
Itulah tepatnya yang saya katakan. Pelarian URI harus menguraikan URL, memisahkan apa yang dianggapnya sebagai parameter individu, melarikan diri, dan menyatukannya kembali. Bahkan itu bisa berantakan. Tapi itu tidak melakukan itu - itu hanya menghindari melarikan diri beberapa karakter sambil melarikan diri dari yang lain, yang membuatnya tidak lengkap. Ini dapat digunakan untuk kasus-kasus sederhana terutama jika Anda tahu bahwa parameter Anda tidak akan .. membingungkan.
Gerard ONeill
0

CGI.escape adalah untuk keluar dari nilai URL dalam string kueri. Semua karakter yang tidak termasuk dalam ALPHA, DIGIT, '_', '-', '.' dan '' rangkaian karakter diloloskan.

Tetapi itu akan membuat URL salah, karena url perlu memiliki '/', ':', '?', '[', '&', '=', Dan ';'. Mungkin lebih dari yang saya tidak bisa memikirkan bagian atas kepala saya.

URI.escape meninggalkan karakter URL itu sendiri, dan mencoba menemukan kunci dan nilai string kueri untuk melarikan diri. Namun ini benar-benar tidak dapat diandalkan karena nilai dapat memiliki semua jenis karakter yang mencegah pelarian yang mudah. Pada dasarnya, ini sudah terlambat. Tetapi jika URL dapat dijadikan sederhana (tidak ada '&' dan '=' dll dalam nilai-nilai), fungsi ini dapat digunakan untuk keluar dari karakter yang mungkin tidak terbaca atau ilegal.

Secara umum - selalu gunakan CGI.escape pada kunci dan nilai individual sebelum bergabung dengan '&' dan menambahkannya setelah '?'.

Gerard ONeill
sumber
0

CGI.escape tidak berfungsi dengan OpenProject API. Itu mengkodekan [] ,: dan bukan +. Saya meretas ini bersama yang tampaknya bekerja sejauh ini untuk OpenProject's API. Tapi saya yakin itu kehilangan beberapa .gsub. Kemungkinannya hampir sama buruknya dengan URI.escape, tetapi itu tidak akan memberi Anda kesalahan yang usang.

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\"]")

Kedua output:

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

Brett
sumber