Jaka jest różnica między EscapeUriString i EscapeDataString?

193

Jeśli tylko zajmujesz się kodowaniem adresów URL, powinienem użyć EscapeUriString ?

użytkownik496949
źródło
10
Zawsze unikaj każdej wartości za pomocą Uri.EscapeDataString(), jak wyjaśniono w odpowiedzi @ Livven. W przypadku innych podejść system po prostu nie ma wystarczających informacji, aby uzyskać zamierzony wynik dla każdego możliwego wkładu.
Timo

Odpowiedzi:

112

Używaj EscapeDataStringzawsze (więcej informacji na temat przyczyny znajduje się poniżej w odpowiedzi Livven )

Edycja : usunięto martwy link do różnic między kodowaniem

Jcl
źródło
3
Nie jestem pewien, czy ten link faktycznie dostarcza więcej informacji, ponieważ dotyczy raczej odblokowywania niż esakapowania.
Steven
1
To w zasadzie ta sama różnica. Jeśli faktycznie czytasz ten artykuł, wokół środka znajduje się tabela, która faktycznie ucieka (nie odblokowuje), aby pokazać różnice (w porównaniu z nimi URLEncode).
Jcl
2
Nadal nie jest dla mnie jasne - co jeśli nie będę uciekał z całego URI, ale tylko jego część - (tj. Dane parametru ciągu zapytania)? Czy uciekam od danych dla identyfikatora URI, czy może EscapeDataString sugeruje coś zupełnie innego?
BrainSlugs83
4
... czy niektóre testy wyglądają tak, jakbym chciał EscapeDataString dla parametru URI. Testowałem z ciągiem „I heart C ++” i EscapeUriString nie kodował znaków „+”, po prostu je pozostawił, tak jak jest, EscapeDataString poprawnie przekonwertował je na „% 2B”.
BrainSlugs83
7
To zła odpowiedź. Nigdy nie powinieneś używać EscapeUriString, to nie ma żadnego sensu. Zobacz odpowiedź Livven poniżej (i oceń ją).
Brandon Paddock
242

Nie uznałem istniejących odpowiedzi za zadowalające, więc postanowiłem głębiej zbadać ten problem. Co zaskakujące, odpowiedź jest bardzo prosta:

Nie ma (prawie *) żadnego ważnego powodu, aby kiedykolwiek używać Uri.EscapeUriString. Jeśli potrzebujesz procentowo zakodować ciąg, zawsze używaj Uri.EscapeDataString.

* Patrz ostatni akapit dla ważnego przypadku użycia.

Dlaczego to? Zgodnie z dokumentacją :

Użyj metody EscapeUriString, aby przygotować nieskalowany ciąg URI, który będzie parametrem dla konstruktora Uri.

To naprawdę nie ma sensu. Zgodnie z RFC 2396 :

Identyfikator URI jest zawsze w formie „ucieczki”, ponieważ ucieczka lub odblokowanie ukończonego URI może zmienić jego semantykę.

Chociaż cytowany RFC został zdezaktualizowany przez RFC 3986 , kwestia nadal jest ważna. Sprawdźmy to, patrząc na konkretne przykłady:

  1. Masz prosty identyfikator URI, taki jak ten:

    http://example.org/

    Uri.EscapeUriString nie zmieni tego.

  2. Zdecydujesz się ręcznie edytować ciąg zapytania bez uwzględnienia zmiany znaczenia:

    http://example.org/?key=two words

    Uri.EscapeUriString (poprawnie) wydostanie się z miejsca dla ciebie:

    http://example.org/?key=two%20words
  3. Zdecydujesz się ręcznie edytować ciąg zapytania jeszcze dalej:

    http://example.org/?parameter=father&son

    Jednak ten ciąg nie jest zmieniany przez Uri.EscapeUriString, ponieważ zakłada, że ​​ampersand oznacza początek innej pary klucz-wartość. To może, ale nie musi być to, co zamierzałeś.

  4. Decydujesz, że tak naprawdę chcesz, aby keyparametr był father&son, więc naprawiasz poprzedni adres URL ręcznie, usuwając znak ampersand:

    http://example.org/?parameter=father%26son

    Jednak Uri.EscapeUriStringucieknie również od znaku procentu, co prowadzi do podwójnego kodowania:

    http://example.org/?parameter=father%2526son

Jak widać, użycie Uri.EscapeUriStringzgodnie z przeznaczeniem uniemożliwia użycie &jako części klucza lub wartości w ciągu zapytania zamiast jako separatora między wieloma parami klucz-wartość.

Wynika to z tego, że próbując uczynić go odpowiednim do ucieczki pełnych identyfikatorów URI, ignoruje znaki zarezerwowane i unika tylko znaków, które nie są zastrzeżone ani niezarezerwowane, co BTW jest sprzeczne z dokumentacją . W ten sposób nie kończy się coś takiego http%3A%2F%2Fexample.org%2F, ale kończy się to przedstawionymi powyżej problemami.


Ostatecznie, jeśli twój identyfikator URI jest prawidłowy, nie musi być poprzedzany znakiem ucieczki, aby mógł zostać przekazany jako parametr do konstruktora Uri, a jeśli nie jest prawidłowy, wywołanie również Uri.EscapeUriStringnie jest magicznym rozwiązaniem. W rzeczywistości będzie działać w wielu, jeśli nie w większości przypadków, ale w żadnym wypadku nie jest niezawodny.

Zawsze należy konstruować adresy URL i ciągi zapytań, gromadząc pary klucz-wartość i kodowanie procentowe, a następnie łącząc je z niezbędnymi separatorami. Możesz użyć Uri.EscapeDataStringdo tego celu, ale nie Uri.EscapeUriString, ponieważ nie ucieka on od zarezerwowanych znaków, jak wspomniano powyżej.

Tylko wtedy, gdy nie możesz tego zrobić, np. W przypadku identyfikatorów URI podanych przez użytkownika, ma sens zastosowanie Uri.EscapeUriStringw ostateczności. Obowiązują jednak wspomniane wcześniej zastrzeżenia - jeśli podany przez użytkownika identyfikator URI jest niejednoznaczny, wyniki mogą być niepożądane.

Livven
źródło
4
Wow, dziękuję za wyjaśnienie tego problemu. Dwie poprzednie odpowiedzi nie były zbyt pomocne.
EverPresent
3
Dokładnie tak. EscapeUriString (podobnie jak domyślne zachowanie EscapeUrl w Win32) został stworzony przez kogoś, kto nie rozumiał identyfikatorów URI lub uciekł. To błędna próba stworzenia czegoś, co wymaga zniekształconego identyfikatora URI, a czasem przekształcenia go w zamierzoną wersję. Ale nie ma informacji potrzebnych do tego, aby zrobić to niezawodnie. Często jest również używany zamiast EscapeDataString, co również jest bardzo problematyczne. Chciałbym, żeby EscapeUriString nie istniał. Każde użycie tego jest złe.
Brandon Paddock,
4
ładnie wyjaśnione +1, to jest znacznie lepsze niż zaakceptowany link tylko odpowiedź
Ehsan Sajjad
1
Ta odpowiedź wymaga więcej uwagi. To jest właściwy sposób, aby to zrobić. Inne odpowiedzi mają scenariusze, w których nie przynoszą zamierzonych rezultatów.
Timo
1
... Pewnie, że encodeURI/ Uri.EscapeUriStringnie jest potrzebny tak często jak encodeURIComponent/ Uri.EscapeDataString(od kiedy masz do czynienia z ślepymi adresami URL, które muszą być używane w kontekście URI), ale to nie znaczy, że nie ma swojego miejsca.
Crescent Fresh
56

Znaki plus (+) mogą wiele powiedzieć o różnicy między tymi metodami. W prostym URI znak plus oznacza „spację”. Rozważ zapytanie do Google o „szczęśliwego kota”:

https://www.google.com/?q=happy+cat

To prawidłowy identyfikator URI (spróbuj) i EscapeUriStringnie będzie go modyfikować.

Teraz rozważ zapytanie Google o „happy c ++”:

https://www.google.com/?q=happy+c++

To jest poprawny URI (spróbuj), ale powoduje wyszukiwanie „szczęśliwego c”, ponieważ dwie plusy są interpretowane jako spacje. Aby to naprawić, możemy przekazać „happy c ++” do EscapeDataStringi voila * :

https://www.google.com/?q=happy+c%2B%2B

*) Zakodowany ciąg danych to tak naprawdę „happy% 20c% 2B% 2B”; % 20 to hex dla znaku spacji, a% 2B to hex dla znaku plus.

Jeśli używasz tego, UriBuilderco powinieneś, musisz EscapeDataStringwłaściwie uciec tylko niektórych składników całego identyfikatora URI. Odpowiedź @ Livven na to pytanie dalej dowodzi, że tak naprawdę nie ma powodu, aby z tego korzystać EscapeUriString.

Seth
źródło
Dzięki. Na przykład, jeśli masz bezwzględny ciąg URI, który musisz zakodować "https://www.google.com/?q=happy c++". Wygląda na to, że muszę ręcznie podzielić na „?”, Czy jest lepszy sposób?
wensveen,
Jeśli przekazujesz cały adres URL jako parametr do innego adresu URL, użyj EscapeDataString. Jeśli podany adres URL jest rzeczywistym adresem URL, to tak, po prostu chcesz się podzielić ?.
Seth
7

Komentarze w źródle wyraźnie odnoszą się do różnicy. Dlaczego te informacje nie są przekazywane za pośrednictwem komentarzy do dokumentacji XML, jest dla mnie zagadką.

EscapeUriString:

Ta metoda pozwoli uniknąć dowolnego znaku, który nie jest znakiem zastrzeżonym lub niezarezerwowanym, w tym znaków procentu. Pamiętaj, że EscapeUriString również nie uniknie znaku „#”.

EscapeDataString:

Ta metoda pozwoli uniknąć dowolnego znaku, który nie jest znakiem bez zastrzeżeń, w tym znaków procentu.

Różnica polega na tym, jak radzą sobie z zastrzeżonymi znakami. EscapeDataStringucieka im; EscapeUriStringnie.

Zgodnie z RFC zarezerwowanymi znakami są::/?#[]@!$&'()*+,;=

Dla kompletności, niezarezerwowane znaki są alfanumeryczne i -._~

Obie metody unikają znaków, które nie są ani zarezerwowane, ani zastrzeżone.

Nie zgadzam się z ogólnym pojęciem, które EscapeUriStringjest złe. Myślę, że metoda, która pozwala na uniknięcie tylko niedozwolonych znaków (takich jak spacje) i niezastrzeżonych znaków, jest przydatna. Ale ma dziwactwo w tym, jak radzi sobie z %postacią. Znaki zakodowane w procentach ( %po których następują 2 cyfry szesnastkowe) są poprawne w URI. Myślę, że EscapeUriStringbyłoby znacznie bardziej przydatne, gdyby wykrył ten wzorzec i uniknął kodowania, %gdy natychmiast poprzedzają go 2 cyfry szesnastkowe.

Todd Menier
źródło
1

Prosty przykład

var data = "example.com/abc?DEF=あいう\x20えお";

Console.WriteLine(Uri.EscapeUriString(data));
Console.WriteLine(Uri.EscapeDataString(data));
Console.WriteLine(System.Net.WebUtility.UrlEncode(data));
Console.WriteLine(System.Web.HttpUtility.UrlEncode(data));

/*
=>
example.com/abc?DEF=%E3%81%82%E3%81%84%E3%81%86%20%E3%81%88%E3%81%8A
example.com%2Fabc%3FDEF%3D%E3%81%82%E3%81%84%E3%81%86%20%E3%81%88%E3%81%8A
example.com%2Fabc%3FDEF%3D%E3%81%82%E3%81%84%E3%81%86+%E3%81%88%E3%81%8A
example.com%2fabc%3fDEF%3d%e3%81%82%e3%81%84%e3%81%86+%e3%81%88%e3%81%8a
*/
Uczenie się
źródło