Co jest ważne, a czego nie ma w zapytaniu URI?

100

Tło (pytanie poniżej)

Szukałem tego w Google w tę iz powrotem, czytając RFC i pytania SO próbując to złamać, ale nadal nie mam gniazda.

Więc myślę, że po prostu głosujemy na „najlepszą” odpowiedź i to wszystko, prawda?

Zasadniczo sprowadza się to do tego.

3.4. Składnik zapytania

Składnik zapytania to ciąg informacji, które mają być zinterpretowane przez zasób.

query = *uric

W składniku zapytania znaki „;”, „/”, „?”, „:”, „@”, „&”, „=”, „+”, „,” I „$” są zarezerwowane.

Pierwszą rzeczą, która mnie zaskakuje, jest to, że * uric jest zdefiniowany w ten sposób

uric = reserved | unreserved | escaped

reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","

Jest to jednak nieco wyjaśnione w akapitach, takich jak

„Zarezerwowana” klasa składni powyżej odnosi się do tych znaków, które są dozwolone w obrębie URI, ale które mogą być niedozwolone w określonym składniku ogólnej składni URI; służą jako ograniczniki elementów opisanych w sekcji 3.

Znaki w zestawie „zarezerwowanym” nie są zarezerwowane we wszystkich kontekstach. Zestaw znaków faktycznie zarezerwowanych w ramach dowolnego składnika URI jest definiowany przez ten składnik. Ogólnie znak jest zarezerwowany, jeśli semantyka identyfikatora URI ulegnie zmianie, jeśli znak zostanie zastąpiony jego kodowaniem ze znakami ucieczki US-ASCII.

Ten ostatni fragment wydaje się nieco cofnięty, ale wyraźnie stwierdza, że ​​zarezerwowany zestaw znaków zależy od kontekstu. Jednak 3.4 stwierdza, że ​​wszystkie zastrzeżone znaki są zarezerwowane w komponencie zapytania, jednak jedyną rzeczą, która zmieniłaby tutaj semantykę, jest unikanie znaku zapytania (?), Ponieważ URI nie definiują pojęcia ciągu zapytania.

W tym momencie całkowicie zrezygnowałem z RFC, ale uznałem RFC 1738 za szczególnie interesujący.

Adres URL HTTP ma postać:

http://<host>:<port>/<path>?<searchpart>

W składnikach <path> i <searchpart>, „/”, „;”, „?” są zastrzeżone. Znak „/” może być użyty w HTTP do określenia struktury hierarchicznej.

Interpretuję to przynajmniej w odniesieniu do adresów URL HTTP, które RFC 1738 zastępuje RFC 2396. Ponieważ zapytanie URI nie ma pojęcia ciągu zapytania, również interpretacja zarezerwowanego tak naprawdę nie pozwala mi definiować ciągów zapytań, tak jak jestem przyzwyczajony już robię.

Pytanie

Wszystko zaczęło się, gdy chciałem przekazać listę numerów wraz z prośbą o inny zasób. Nie myślałem o tym zbyt wiele i po prostu przekazałem to jako wartości oddzielone przecinkami. Ku mojemu zdziwieniu udało się jednak uniknąć przecinka. page.html?q=1,2,3Zakodowane zapytanie page.html?q=1%2C2%2C3działa, ale jest brzydkie i nie spodziewałem się tego. Wtedy zacząłem przeglądać RFC.

Moje pierwsze pytanie brzmi po prostu, czy kodowanie przecinków jest naprawdę konieczne?

Moja odpowiedź, zgodnie z RFC 2396: tak, zgodnie z RFC 1738: nie

Później znalazłem powiązane posty dotyczące przekazywania list między wnioskami. Gdzie podejście CSV było gotowe jako złe. Pojawiło się to zamiast tego (nie widziałem tego wcześniej).

page.html?q=1;q=2;q=3

Moje drugie pytanie, czy to prawidłowy adres URL?

Moja odpowiedź, zgodnie z RFC 2396: nie, zgodnie z RFC 1738: nie (; jest zastrzeżone)

Nie mam żadnych problemów z przekazywaniem csv, o ile jest to liczba, ale tak, istnieje ryzyko konieczności kodowania i dekodowania wartości w tę iz powrotem, jeśli przecinek nagle jest potrzebny do czegoś innego. W każdym razie wypróbowałem ciąg zapytania ze średnikiem dwukropka w ASP.NET i wynik nie był tym, czego się spodziewałem.

Default.aspx?a=1;a=2&b=1&a=3

Request.QueryString["a"] = "1;a=2,3"
Request.QueryString["b"] = "1"

Nie widzę, jak bardzo różni się to od podejścia csv, ponieważ kiedy pytam o „a”, otrzymuję ciąg znaków z przecinkami. ASP.NET z pewnością nie jest implementacją referencyjną, ale jeszcze mnie nie zawiódł.

Ale co najważniejsze - moje trzecie pytanie - gdzie jest specyfikacja tego? i co byś zrobił lub czego nie zrobiłeś?

John Leidegren
źródło
W jaki sposób RFC 1738 może zastąpić RFC 2396, skoro RFC 2396 został opublikowany prawie 4 lata później?
Matthew Flaschen
1
Jeśli chodzi o adresy URL i to, co praktycznie ma sens, według mojej interpretacji tak jest. (zastąpione prawdopodobnie nie jest właściwym słowem, ponieważ zostało użyte w terminologii RFC do wycofania starych specyfikacji RFC, RFC 1738 nie uważa tego za przestarzałe, gdy jest to jedyna specyfikacja, która umożliwia umieszczenie ciągu zapytania w części wyszukiwania adresu URL)
John Leidegren

Odpowiedzi:

69

To, że znak jest zarezerwowany w ogólnym komponencie adresu URL, nie oznacza, że ​​musi on zostać zmieniony, gdy pojawia się w komponencie lub w danych w komponencie. Znak musi być również zdefiniowany jako separator w składni ogólnej lub specyficznej dla schematu, a wygląd znaku musi znajdować się w danych.

Obecnym standardem dla ogólnych identyfikatorów URI jest RFC 3986 , który ma to do powiedzenia:

2.2. Postacie zastrzeżone

Identyfikatory URI obejmują komponenty i podkomponenty, które są rozdzielane znakami z „zarezerwowanego” zestawu. Znaki te nazywane są „zarezerwowanymi”, ponieważ mogą (ale nie muszą) być definiowane jako ograniczniki przez składnię ogólną, składnię specyficzną dla każdego schematu lub składnię specyficzną dla implementacji algorytmu dereferencji identyfikatora URI. Jeśli dane dla składnika URI kolidowałyby z przeznaczeniem znaku zastrzeżonego jako separatora [podkreślenie dodane], dane będące w konflikcie muszą być zakodowane w procentach przed utworzeniem URI.

   zastrzeżone = rozgraniczenia genów / rozgraniczenia podrzędne

   gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"

   sub-delims = „!” / "$" / "&" / "'" / "(" / ")"
               / "*" / "+" / "," / ";" / "="

3.3. Składnik ścieżki

[…]
pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
[…]

3.4 Składnik zapytania

[…]
      zapytanie = * (pchar / "/" / "?")

W związku z tym przecinki są wyraźnie dozwolone w ciągach zapytań i należy je stosować w danych tylko wtedy, gdy określone schematy definiują je jako separator. Schemat HTTP nie używa przecinka ani średnika jako separatora w ciągach zapytań, więc nie trzeba ich używać. To, czy przeglądarki przestrzegają tego standardu, to inna sprawa.

Używanie CSV powinno działać dobrze w przypadku danych ciągów, wystarczy przestrzegać standardowych konwencji CSV i albo cytować dane, albo wstawiać przecinki z ukośnikami odwrotnymi.

Jeśli chodzi o RFC 2396, zezwala również na stosowanie przecinków bez znaku zmiany znaczenia w ciągach zapytań HTTP:

2.2. Postacie zastrzeżone

Wiele identyfikatorów URI zawiera komponenty składające się z pewnych znaków specjalnych lub rozdzielane nimi. Znaki te nazywane są „zastrzeżonymi”, ponieważ ich użycie w składniku URI jest ograniczone do ich zastrzeżonego celu. Jeśli dane składnika URI byłyby w konflikcie z zarezerwowanym celem, to dane będące w konflikcie muszą zostać zabezpieczone przed utworzeniem URI.

Ponieważ przecinki nie mają zarezerwowanego celu w schemacie HTTP, nie muszą być używane w danych. Uwaga z § 2.3 o zastrzeżonych znakach to te, które zmieniają semantykę, gdy kodowane procentowo mają zastosowanie tylko ogólnie; znaki mogą być kodowane procentowo bez zmiany semantyki dla określonych schematów, a mimo to nadal mogą być zastrzeżone.

poza
źródło
23

Aby odpowiedzieć na pytanie, co jest prawidłowe w ciągu zapytania, sprawdziłem, które znaki specjalne są zastępowane przez chrome podczas wysyłania żądania:

Space -> %20
! -> !
" -> %22
# -> removed, marks the end of the query string
% -> %
& -> &
' -> %27
( -> (
) -> )
* -> *
+ -> + (this usually means blank when received at the server, so encode if necessary)
, -> ,
- -> -
. -> .
/ -> /
: -> :
; -> ;
< -> %3C
= -> =
> -> %3E
? -> ?
@ -> @
[ -> [
\ -> \
] -> ]
^ -> ^
_ -> _
` -> `
{ -> {
| -> |
} -> }
~ -> ~

Extended ASCII (like °) -> Every character from this set is encoded

Uwaga: prawdopodobnie nie oznacza to, że nie powinieneś zmieniać znaczenia znaków, które nie zostały zastąpione podczas generowania identyfikatorów URI dla linków. Na przykład często zaleca się, aby nie używać ~w identyfikatorach URI z powodu problemów ze zgodnością, ale nadal jest to prawidłowy znak.

Innym przykładem może być znak plus, który jest prawidłowy, ale zwykle traktowany jako zakodowany pusty, gdy serwer odbiera go jako część żądania. Dlatego powinien być zakodowany, nawet jeśli jest ważny, gdy jego celem jest reprezentowanie plusa, a nie spacji.

Tak więc, aby odpowiedzieć, co powinno być zakodowane: nieprawidłowe znaki i znaki, które chcesz traktować dosłownie, ale mają specjalne znaczenie lub mogą powodować problemy po stronie serwera.

user764754
źródło
Czy /programming/2366260/whats-valid-and-whats-not-in-a-uri-query?param=b#1;c#2jest prawidłowym parametrem zapytania?
Sumit Jain
@SumitJain Nie, ponieważ #nie może pojawić się wewnątrz części zapytania identyfikatora URI w obecnej postaci. Będziesz musiał zakodować go jako %23, więc powinien być URI /programming/2366260/whats-valid-and-whats-not-in-a-uri-query?param=b%231;c%232.
Dai
10

Po prostu użyj ?q=1+2+3

Odpowiadam tutaj na czwarte pytanie :) które nie pytałem ale wszystko zaczęło się od: jak przekazać listę liczb a-la wartości rozdzielane przecinkami? Wydaje mi się, że najlepszym podejściem jest po prostu podanie ich rozdzielonych spacjami, gdzie spacje zostaną zakodowane w postaci adresu URL +. Działa świetnie, o ile wiesz, że wartości na liście nie zawierają spacji (coś, czego nie mają liczby).

Nas Banov
źródło
Chociaż powinien to być komentarz (ponieważ nie odpowiada na pytanie), dziękuję. +ma jeszcze większy sens w tym konkretnym przypadku, gdy szukałem przecinka.
Gajus
6

page.html? q = 1; q = 2; q = 3

czy to jest prawidłowy adres URL?

Tak. ;Jest zarezerwowany, ale nie przez RFC. Kontekstem definiującym ten komponent jest definicja application/x-www-form-urlencodedtypu mediów, która jest częścią standardu HTML (sekcja 17.13.4.1 ). W szczególności podstępna notatka ukryta w sekcji B.2.2 :

Zalecamy, aby firmy implementujące serwer HTTP, aw szczególności implementujące CGI, obsługiwały użycie ";" zamiast „&”, aby oszczędzić autorom kłopotu z unikaniem znaków „&” w ten sposób.

Niestety wiele popularnych struktur skryptów po stronie serwera, w tym ASP.NET, nie obsługuje tego zastosowania.

bobince
źródło
Więc chociaż ?q=1;q=2;q=3zapytanie jest poprawne, jest niejednoznaczne: niektóre frameworki po stronie serwera odczytują je w znaczeniu { q: '1;q=2;q=3' }, inne mogą to robić podobnie { q: {'1', '2', '3'}}.
Nas Banov
1
Tak. Co gorsza, HTML5 nie zawiera teraz języka „about” ;, co oznacza, że ​​HTML4 i HTML5 są niespójne. Ugh, niebezpieczeństwa nienormatywnego języka w dokumencie specyfikacji ...
bobince
@NasBanov A jeszcze inni (np. PHP) zinterpretują to jako{ q: 3 }
Nicholas Shanks
1
@NicholasShanks - w przypadku PHP wszystkie zakłady są wyłączone! :)
Nas Banov
1

Chciałbym zauważyć, że page.html?q=1&q=2&q=3jest to również prawidłowy adres URL. Jest to całkowicie uzasadniony sposób wyrażania tablicy w ciągu zapytania. Technologia Twojego serwera określi, jak dokładnie to zostanie przedstawione.

W klasycznym ASP sprawdzasz, Response.QueryString("q").Counta następnie używasz Response.QueryString("q")(0)(i (1) i (2)).

Zauważ, że widziałeś to również w swoim ASP.NET (myślę, że nie było to zamierzone, ale spójrz):

Default.aspx?a=1;a=2&b=1&a=3

Request.QueryString["a"] = "1;a=2,3"
Request.QueryString["b"] = "1"

Zauważ, że średnik jest ignorowany, więc tak a zdefiniowałeś dwa razy i otrzymałeś jego wartość dwukrotnie, oddzieloną przecinkiem. Użycie wszystkich znaków ampersandów Default.aspx?a=1&a=2&b=1&a=3da w wyniku a„1,2,3”. Ale jestem pewien, że istnieje metoda uzyskania każdego pojedynczego elementu, na wypadek, gdyby same elementy zawierały przecinki. Jest to po prostu domyślna właściwość nieindeksowanego QueryString, która łączy wartości podrzędne razem z separatorami przecinkowymi.

ErikE
źródło
1

Miałem ten sam problem. Adres URL, do którego nastąpiło hiperłącze, był adresem URL firmy zewnętrznej i oczekiwał listy parametrów page.html?q=1,2,3TYLKO w formacie oraz adresu URLpage.html?q=1%2C2%2C3 nie działał. Udało mi się to uruchomić za pomocą javascript. Może nie jest to najlepsze podejście, ale może sprawdzić tutaj rozwiązanie , jeśli komukolwiek pomoże.

ciąć
źródło
-3

Jeśli wysyłasz ZAKODOWANE znaki do pliku FLASH / SWF , powinieneś KODOWAĆ znak dwukrotnie !! (z powodu parsera Flash)

T.Todua
źródło