Jaka jest różnica między URI.escape a CGI.escape?

147

Jaka jest różnica między URI.escapei, CGI.escapea którego należy użyć?

Tom Lehman
źródło

Odpowiedzi:

124

Były pewne drobne różnice, ale ważne jest to, że URI.escapezostała zaniechana w Ruby 1.9.2 ... więc używać CGI::escapelub ERB :: Util.url_encode .

Dla zainteresowanych istnieje długa dyskusja na temat ruby-core, w której wspomina się również o WEBrick :: HTTPUtils.escape i WEBrick :: HTTPUtils.escape_form .

Marc-André Lafortune
źródło
11
Żeby tylko dodać zamieszania - właśnie zobaczyłem komentarz na stackoverflow.com/questions/4967608/, gdzie ktoś wspomniał, że cgi escape używa '+' zamiast% 20 dla spacji i że jest to sprzeczne ze 'specyfikacją' ...
Louis Sayers,
18
alternatywą jest użycie ERB::Util.url_encodetego, co właściwie używa %20 do spacji
riffraff
1
@Ernest: See: github.com/ruby/ruby/commit/… (aktualizacja odpowiedzi)
Marc-André Lafortune
4
ruby-doc.org/stdlib-2.0.0/libdoc/uri/rdoc/URI/Escape.html . W Ruby 2.0.0 znajduje się moduł URI.escape. Dlaczego został wycofany?
user938363
1
@ user938363 Jeśli klikniesz tam źródło pokazu, zobaczysz, że jest nadal oznaczone jako przestarzałe.
drewish
229

Jaka jest różnica między toporem a mieczem i którego powinienem użyć? To zależy od tego, co musisz zrobić.

URI.escapemiał zakodować ciąg znaków (URL) w tzw. „ kodowaniu procentowym ”.

CGI::escapepochodzi ze specyfikacji CGI , która opisuje sposób kodowania / dekodowania danych między serwerem WWW a aplikacją.

Teraz załóżmy, że musisz uciec przed identyfikatorem URI w swojej aplikacji. Jest to bardziej szczegółowy przypadek użycia. W tym celu społeczność Ruby używana URI.escapeprzez lata. Problem URI.escapepolegał na tym, że nie mógł obsłużyć specyfikacji 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 został oznaczony jako przestarzały:

Ponadto obecny kod URI.encode jest prosty gsub. Ale myślę, że powinno podzielić URI na komponenty, następnie uciec przed każdym komponentem i ostatecznie połączyć je.

Dlatego obecny kod URI.encode jest uważany za szkodliwy i przestarzały. Zostanie to usunięte lub drastycznie zmieni zachowanie.

Jaki jest w tej chwili zamiennik?

Jak powiedziałem powyżej, obecny kod URI.encode jest nieprawidłowy na poziomie specyfikacji. Dlatego nie zapewnimy dokładnej wymiany. Wymiana będzie się różnić w zależności od przypadku użycia.

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

Niestety w dokumentach nie ma ani słowa na ten temat, jedynym sposobem, aby się o tym dowiedzieć, jest sprawdzenie źródła lub uruchomienie skryptu z ostrzeżeniami na poziomie szczegółowym ( -wW2) (lub użycie jakiegoś google-fu).

Niektórzy proponowali użycie CGI::Escapejako parametrów zapytania, ponieważ nie można było uciec przed całym identyfikatorem 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::escapepowinien być używany tylko dla parametrów zapytania, ale wyniki będą ponownie niezgodne ze specyfikacją. W rzeczywistości najczęstszym przypadkiem użycia jest ucieczka przed danymi z formularza, na przykład podczas wysyłania application/x-www-form-urlencodedżądania POST.

Wspomniano również o WEBrick::HTTPUtils.escapeniewielkiej poprawie (znowu to po prostu prosta gsub, czyli IMO, nawet gorsza opcja niż 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" 

Najbardziej zbliżony do specyfikacji wydaje się klejnot adresowalny :

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"

Zwróć uwagę, że w przeciwieństwie do wszystkich poprzednich opcji, Adresowalne nie zmienia znaczenia #i jest to oczekiwane zachowanie. chcesz zachować #skrót w ścieżce URI, ale nie w zapytaniu URI.

Jedynym problemem, jaki pozostał, jest to, że nie pominęliśmy poprawnie naszych parametrów zapytania, co prowadzi nas do wniosku: nie powinniśmy używać jednej metody dla całego identyfikatora URI, ponieważ nie ma (jak dotąd) idealnego rozwiązania. Jak widzisz, &nie został zmieniony z „Mój blog i Twój blog”. Musimy użyć innej formy ucieczki dla parametrów zapytania, gdzie użytkownicy mogą umieszczać różne znaki o specjalnym znaczeniu w adresach URL. Wpisz kodowanie adresu URL. Kodowanie adresu URL powinno być używane dla każdej „podejrzanej” wartości zapytania, podobnie jak w przypadku ERB::Util.url_encode:

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

To fajne, ale już wymagaliśmy Adresowalny:

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"

Wniosek:

  • Nie używaj URI.escapeani podobnych
  • Użyj, CGI::escapejeśli potrzebujesz tylko ucieczki z formy
  • Jeśli potrzebujesz pracować z URI, użyj Adresowalny, oferuje kodowanie adresów URL, kodowanie formularzy i normalizuje adresy URL.
  • Jeśli jest to projekt Railsów, zajrzyj do sekcji „ Jak zmienić znaczenie łańcucha znaków w URL-u w Railsach?
Ernest
źródło
Dziękuję bardzo za informacje. Z pewnością pozbył się niektórych ostrzeżeń dotyczących testowania motyki. Grabie i motyka wyglądają poniżej.
Douglas G. Allen
Świetne wyjaśnienie @Ernest, ale problem polega na tym, że nie będzie działać w przypadku zewnętrznych adresów URL, których nie próbuję utworzyć (i nad którymi nie mam kontroli). np. roboty, które odczytują adresy URL ze strony internetowej, a następnie próbują uzyskać do nich dostęp (które muszą być zakodowane przed uzyskaniem dostępu).
amit_saxena
@amit_saxena, jeśli możesz sobie pozwolić na posiadanie Addressablejednego ze swoich klejnotów, możesz najpierw przeanalizować adres URL, fi rubydoc.info/gems/addressable/Addressable/URI.heuristic_parse
Ernest
Ciekawy! Ale znowu, nie mogę uzyskać skrótu parametrów z oryginalnego adresu URL za pomocą tego, który następnie koduję tak, jak opisujesz. Przepływ w moim przypadku jest następujący: Otrzymuję zewnętrzne adresy URL z jakiegoś źródła -> które muszę następnie zakodować -> Przekaż do klienta http, aby pobrać zawartość. Jeśli nie zakoduję poprawnie zewnętrznych adresów URL, klienci HTTP oparte na Ruby zawiodą i wyświetlą nieprawidłowe błędy URI.
amit_saxena
@amit_saxena parse metoda zwróci instancję Addressable:URL, możesz wtedy wywołać wszystkie metody instancji na niej, może jedna z nich przyniesie pożądane rezultaty: rubydoc.info/gems/addressable/Addressable/URI
Ernest
6

CGI::escapejest dobry do zmiany znaczenia segmentów tekstu, aby można ich było używać w parametrach zapytania URL (ciągi znaków po znaku „?”). Na przykład, jeśli chcesz, aby parametr zawierał znaki ukośnika w adresie URL, najpierw CGI :: zmieniamy znaczenie tego ciągu, a następnie wstawiamy go do adresu URL.

Jednak w Railsach prawdopodobnie nie będziesz go używać bezpośrednio. Zwykle używasz tego hash.to_param, który będzie używany CGI::escapepod maską.


URI::escapejest dobry do ucieczki przed adresem URL, który nie został poprawnie zmieniony. Na przykład niektóre witryny wyświetlają nieprawidłowy adres URL bez zmiany znaczenia w tagu kotwicy. Jeśli Twój program używa tych adresów URL do pobierania większej liczby zasobów, OpenURI będzie narzekać, że adresy URL są nieprawidłowe. Musisz URI::escapeto zrobić, aby był prawidłowym adresem URL. Jest więc używany do zmiany znaczenia całego ciągu URI, aby był poprawny. Moim słowem URI :: unescape czyni adres URL czytelnym dla człowieka, a URI :: escape czyni go poprawnym dla przeglądarek.

To jest termin mojego laika i nie krępuj się go poprawić.

lulalala
źródło
1

Różnica polega na tym, że URI.escape nie działa ...

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

URI.escape"/en/test?asd=qwe"
=> "/en/test?asd=qwe"
Radu Simionescu
źródło
2
Wybrałeś niewłaściwy przypadek testowy. / S,? I = są częścią prawidłowego identyfikatora URI i dlatego nie są używane. Inne znaki, które wymagają zmiany znaczenia, szczególnie w ciągu zapytania, powinny być.
Gerard ONeill
@GerardONeill Wybrałem przypadek testowy, aby dokładnie pokazać, jak URI.escape nie działa i jest zawodny. Czy sugerujesz, że URI.escape ma uciekać tylko przed ciągiem zapytania? jak może stwierdzić, kiedy wartość parametru jest zakończona, jeśli chcę tam zakodować &? może dlatego jest przestarzały?
Radu Simionescu
1
To jest dokładnie to, co mówię. Funkcja ucieczki URI musi przeanalizować adres URL, oddzielić, jak uważa, poszczególne parametry, zmienić ich znaczenie i złożyć je z powrotem. Nawet to może być brudne. Ale tego nie robi - po prostu unika ucieczki niektórym postaciom podczas ucieczki przed resztą, co powoduje, że jest niekompletny. Może być używany w prostych przypadkach, szczególnie jeśli wiesz, że twoje parametry nie będą ... mylące.
Gerard ONeill
0

CGI.escape służy do zmiany znaczenia adresu URL w ciągu zapytania. Wszystkie znaki, które nie należą do ALPHA, DIGIT, '_', '-', ''. i zestaw znaków „” są chronione.

Ale to spowodowałoby, że adres URL byłby nieprawidłowy, ponieważ adres URL musi zawierać znaki „/”, „:”, „?”, „[”, „&”, „=” I „;”. Może więcej, niż nie mogę wymyślić z czubka głowy.

URI.escape pozostawia te znaki adresu URL w spokoju i próbuje znaleźć klucze ciągu zapytania i wartości, które mają zostać usunięte. Jednak naprawdę nie można na tym polegać, ponieważ wartości mogą mieć różne rodzaje znaków, uniemożliwiające łatwą ucieczkę. Zasadniczo jest już za późno. Ale jeśli można polegać na prostocie adresu URL (brak znaków „&” i „=” itp. W wartościach), ta funkcja może zostać użyta do uniknięcia być może nieczytelnych lub niedozwolonych znaków.

Ogólnie - zawsze używaj CGI.escape na poszczególnych kluczach i wartościach przed połączeniem ich za pomocą „&” i dodaniem ich po „?”.

Gerard ONeill
źródło
0

CGI.escape nie działał z API OpenProject. Zakodował [], a nie +. Zhakowałem to razem, co wydaje się działać do tej pory dla API OpenProject. Ale jestem pewien, że brakuje niektórych plików .gsub. Prawdopodobnie jest prawie tak zły, jak URI.escape, ale nie da ci przestarzałych błędów.

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

Oba wyjścia:

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

Brett
źródło