HTTP GET z treścią żądania

2109

Tworzę nową usługę RESTful dla naszej aplikacji.

Podczas wykonywania GET na niektórych obiektach klienci mogą żądać zawartości tego obiektu. Jeśli chcą dodać niektóre parametry (na przykład sortując listę), mogą dodać te parametry w ciągu zapytania.

Alternatywnie chcę, aby ludzie mogli określić te parametry w treści żądania. HTTP / 1.1 nie wydaje się tego wyraźnie zabraniać. Pozwoli im to określić więcej informacji, może ułatwić określenie złożonych żądań XML.

Moje pytania:

  • Czy to w ogóle dobry pomysł?
  • Czy klienci HTTP będą mieli problemy z używaniem treści żądania w ramach żądania GET?

http://tools.ietf.org/html/rfc2616

Wynicować
źródło
552
Zaletą jest to, że umożliwia łatwe wysyłanie treści żądania XML lub JSON, nie ma ograniczenia długości i jest łatwiejsze do kodowania (UTF-8).
Evert
29
Jeśli szukasz bezpiecznej i idempotentnej metody pozwalającej na wysyłanie treści do żądań, możesz spojrzeć na SZUKAJ, PROPFIND i RAPORT. Oczywiście nieużywanie GET i posiadanie ciała żądania w większym stopniu ogranicza buforowanie.
Julian Reschke,
226
@fijiaaron: To 3 lata później i od tego czasu mam duże doświadczenie w pisaniu serwisów internetowych. To właściwie wszystko, co robiłem przez ostatnie kilka lat. Mogę śmiało powiedzieć, że dodawanie treści do żądania GET jest naprawdę bardzo złym pomysłem. Dwie górne odpowiedzi stoją jak kamień.
Evert
26
@Ellesedil: Mówiąc najprościej: wszelkie zalety, jakie istnieją przy korzystaniu z GET nad POST, istnieją ze względu na sposób zaprojektowania HTTP. Korzyści te już nie istnieją, jeśli w ten sposób naruszysz standard. Dlatego został tylko jeden powód, aby używać GET + zamiast żądania POST: Estetyka. Nie poświęcaj solidnego projektu ponad estetykę.
Evert
7
Aby podkreślić to, co powiedział Evert: „nie ma ograniczenia długości”. Jeśli Twój GET z parametrami zapytania łamie ograniczenie długości (2048), to jaki jest inny wybór niż umieszczenie informacji o ciągu zapytania w obiekcie json, na przykład w treści żądania.
Kieran Ryan

Odpowiedzi:

1724

Komentarz Roya Fieldinga dotyczący włączenia ciała z prośbą GET .

Tak. Innymi słowy, każda wiadomość z żądaniem HTTP może zawierać treść wiadomości, a zatem musi analizować wiadomości w tym celu. Semantyka serwera dla GET jest jednak ograniczona w taki sposób, że treść, jeśli istnieje, nie ma znaczenia semantycznego dla żądania. Wymagania dotyczące analizowania są oddzielone od wymagań dotyczących semantyki metody.

Tak, tak, możesz wysłać ciało za pomocą GET i nie, nigdy nie jest to przydatne.

Jest to część warstwowego projektu HTTP / 1.1, który ponownie stanie się przejrzysty po podzieleniu specyfikacji (praca w toku).

.... Roy

Tak, możesz wysłać treść żądania za pomocą GET, ale nie powinno to mieć żadnego znaczenia. Jeśli nadasz mu znaczenie, analizując go na serwerze i zmieniając odpowiedź w oparciu o jego zawartość , wówczas ignorujesz to zalecenie w specyfikacji HTTP / 1.1, sekcja 4.3 :

[...] jeśli metoda żądania nie obejmuje zdefiniowanej semantyki dla ciała encji, wówczas treść komunikatu POWINNA zostać zignorowana podczas obsługi żądania.

I opis metody GET w specyfikacji HTTP / 1.1, sekcja 9.3 :

Metoda GET oznacza pobieranie wszelkich informacji ([...]) zidentyfikowanych przez URI żądania.

który stwierdza, że ​​ciało żądania nie jest częścią identyfikacji zasobu w żądaniu GET, tylko identyfikator URI żądania.

Aktualizacja RFC2616 określany jako „specyfikacja HTTP / 1.1” jest już nieaktualny. W 2014 r. Został zastąpiony przez RFC 7230-7237. Cytuj „treść wiadomości POWINNA zostać zignorowana podczas obsługi żądania” została usunięta. Teraz jest to po prostu „Żądanie ramkowania wiadomości jest niezależne od semantyki metody, nawet jeśli metoda nie definiuje żadnego zastosowania dla treści wiadomości” Drugi cytat „Metoda GET oznacza pobieranie wszelkich informacji ... zidentyfikowanych przez URI żądania” został usunięty. - Z komentarza

Paul Morgan
źródło
71
Buforowanie / proxy to dwie rzeczy, które najprawdopodobniej złamiesz, tak. „Semantyka” to po prostu inny sposób powiedzenia „sposób, w jaki ludzie, którzy wytwarzają inne komponenty, będą oczekiwać, że będą działać inne komponenty”. Jeśli naruszysz semantykę, bardziej prawdopodobne jest, że coś się zepsuje w miejscach, w których ludzie pisali rzeczy, które oczekiwały, że będziesz szanował tę semantykę.
Stuart P. Bentley,
108
Elasticsearch jest dość ważnym produktem, który wykorzystuje treści żądania HTTP w GET. Zgodnie z ich instrukcją, czy żądanie HTTP powinno obsługiwać posiadanie treści, czy nie, jest niezdefiniowane. Osobiście nie jestem zadowolony z wypełniania treści żądania GET, ale wydaje się, że mają oni inne zdanie i muszą wiedzieć, co robią. elastic.co/guide/en/elasticsearch/guide/current/...
GordonM
25
@iwein podając treść żądania GET w rzeczywistości nie stanowi naruszenia specyfikacji. HTTP / 1.1 określa, że ​​serwery POWINNY zignorować treść, ale RFC 2119 określa, że ​​implementatorzy mogą ignorować klauzule „POWINIEN”, jeśli mają ku temu dobry powód. Klient raczej narusza specyfikację, jeśli zakłada, że ​​zmiana treści GET nie zmieni odpowiedzi.
Emil Lundberg,
107
RFC2616 określany jako „specyfikacja HTTP / 1.1” jest teraz przestarzały. W 2014 r. Został zastąpiony przez RFC 7230-7237. Cytuj „ treść wiadomości POWINNA zostać zignorowana podczas obsługi żądania ” została usunięta . Teraz jest to po prostu „ Żądanie ramkowania wiadomości jest niezależne od semantyki metody, nawet jeśli metoda nie definiuje żadnego zastosowania dla treści wiadomości ” Drugi cytat „ Metoda GET oznacza pobieranie wszelkich informacji ... zidentyfikowanych przez URI żądania ” został usunięty . Proponuję więc edytować odpowiedź @Jarl
Artem Nakonechny
28
Wiem, że to stara nić - natknąłem się na nią. @Artem Nakonechny ma techniczną rację, ale nowa specyfikacja mówi: „Ładunek w komunikacie żądania GET nie ma zdefiniowanej semantyki; wysłanie treści ładunku na żądanie GET może spowodować, że niektóre istniejące implementacje odrzucą żądanie”. Więc nadal nie jest to dobry pomysł, jeśli można go uniknąć.
fastcatch
289

Chociaż możesz to zrobić, o ile nie jest to wyraźnie wykluczone w specyfikacji HTTP, sugerowałbym unikanie tego tylko dlatego, że ludzie nie oczekują, że coś będzie działać w ten sposób. Łańcuch żądań HTTP składa się z wielu etapów i chociaż „w większości” są one zgodne ze specyfikacją HTTP, jedyną gwarancją jest to, że będą się zachowywać tak, jak zwykle używane są w przeglądarkach internetowych. (Myślę o takich rzeczach, jak przezroczyste proxy, akceleratory, zestawy narzędzi A / V itp.)

Taki jest duch zasady solidności z grubsza „bądź liberalny w tym, co akceptujesz, a konserwatywny w tym, co wysyłasz”, nie chcesz przekraczać granic specyfikacji bez uzasadnionego powodu.

Jeśli jednak masz dobry powód, idź do niego.

beczułka
źródło
228
Zasada solidności jest wadliwa. Jeśli jesteś liberalny w tym, co akceptujesz, dostaniesz gówna, jeśli odniesiesz sukces w zakresie adopcji, tylko dlatego, że akceptujesz gówno. Utrudni to ewolucję interfejsu. Wystarczy spojrzeć na HTML. Taka jest zasada rebelii w akcji.
Eugene Beresovsky,
27
Myślę, że sukces i zasięg przyjęcia (i nadużycia) protokołów świadczy o wartości zasady solidności.
caskey
39
Czy kiedykolwiek próbowałeś parsować prawdziwy HTML? Nie jest możliwe wdrożenie go samemu, dlatego prawie wszyscy - w tym naprawdę duzi gracze, tacy jak Google (Chrome) i Apple (Safari), nie zrobili tego, ale polegali na istniejących implementacjach (ostatecznie wszyscy polegali na KHTML KDE). Ponowne użycie jest oczywiście miłe, ale czy próbowałeś wyświetlać HTML w aplikacji .net? To koszmar, ponieważ albo musisz osadzić - niezarządzany - komponent IE (lub podobny), z jego problemami i awariami, albo używasz dostępnego (zarządzanego przez codeplex) komponentu, który nawet nie pozwala ci zaznaczać tekstu.
Eugene Beresovsky
6
Specyfikacja HTTP nie tylko zezwala na dane treści z żądaniem GET, ale jest to również powszechna praktyka: API _search popularnego silnika ElasticSearch zaleca żądania GET z zapytaniem dołączonym w treści JSON. Jako koncesja na niekompletne implementacje klienta HTTP, umożliwia także żądania POST tutaj.
Christian Pietsch
3
@ChristianPietsch, jest dziś powszechną praktyką. Cztery lata temu tak nie było. Chociaż specyfikacja wyraźnie zezwala klientowi na opcjonalne dołączenie (MAY) encji do żądania (sekcja 7), znaczenie MAY jest zdefiniowane w RFC2119, a (nieładny) serwer proxy może być zgodny ze specyfikacją podczas usuwania jednostek w żądaniach GET, szczególnie dopóki nie ulega awarii, może zapewnić „zmniejszoną funkcjonalność” poprzez przekazywanie nagłówków żądań, a nie dołączonej jednostki. Podobnie istnieje wiele zasad dotyczących tego, jakie zmiany wersji MUSZĄ / MAJ / POWINNY być dokonywane podczas proxy między różnymi poziomami protokołu.
caskey
151

Prawdopodobnie napotkasz problemy, jeśli kiedykolwiek spróbujesz skorzystać z buforowania. Serwery proxy nie będą szukać w treści GET, aby sprawdzić, czy parametry mają wpływ na odpowiedź.

Darrel Miller
źródło
10
Korzystanie z pól nagłówka ETag / Last-Modified pomaga w ten sposób: gdy używany jest „warunkowy GET”, serwery proxy / pamięci podręczne mogą działać na podstawie tych informacji.
jldupont
2
@jldupont Pamięci podręczne używają sprawdzania poprawności, aby sprawdzić, czy nieaktualne odpowiedzi można ponownie sprawdzić, jednak nie są one używane jako część podstawowego lub pomocniczego klucza pamięci podręcznej.
Darrel Miller
Można to naprawić za pomocą sumy kontrolnej treści w parametrze zapytania
Adrian May
73

Ani restclient, ani konsola REST nie obsługują tego, ale curl tak.

Specyfikacja HTTP mówi w sekcji 4.3

Ciało komunikatu NIE MOŻE być zawarte w żądaniu, jeśli specyfikacja metody żądania (sekcja 5.1.1) nie pozwala na wysyłanie ciała jednostki w żądaniach.

Sekcja 5.1.1 przekierowuje nas do sekcji 9.x dla różnych metod. Żaden z nich wyraźnie nie zabrania dołączania treści wiadomości. Jednak...

Sekcja 5.2 mówi

Dokładny zasób zidentyfikowany na podstawie żądania internetowego jest określany poprzez sprawdzenie zarówno URI żądania, jak i pola nagłówka Host.

a sekcja 9.3 mówi

Metoda GET oznacza pobieranie wszelkich informacji (w formie encji) identyfikowanych przez identyfikator URI żądania.

Które razem sugerują, że podczas przetwarzania żądania GET serwer nie musi sprawdzać niczego innego niż pole URI żądania i pole nagłówka Host.

Podsumowując, specyfikacja HTTP nie uniemożliwia wysłania treści wiadomości za pomocą GET, ale istnieje wystarczająca dwuznaczność, która nie zaskoczyłaby mnie, gdyby nie była obsługiwana przez wszystkie serwery.

Dave Durbin
źródło
2
Paw ma również opcję obsługi żądań GET za pomocą treści, ale musi być włączona w ustawieniach.
s.Daniel
„Metoda GET oznacza pobieranie wszelkich informacji (w formie encji) identyfikowanych przez identyfikator URI żądania.” Czy zatem technicznie nielegalne / złe jest posiadanie punktu końcowego GET, który pobiera wszystkie jednostki? Np. GET /contacts/100/addressesZwraca zbiór adresów dla osoby z id=100.
Josh M.,
Sprawdzona biblioteka Java do testowania interfejsów API REST nie obsługuje żądania GET z treścią. Apache HttpClient też go nie obsługuje.
Paulo Merson
Django obsługuje także analizę składni GET
Bruno Finger
60

Elasticsearch akceptuje żądania GET z treścią. Wydaje się nawet, że jest to preferowany sposób: Przewodnik po Elasticsearch

Niektóre biblioteki klienta (np. Sterownik Ruby) mogą rejestrować komendę cry na standardowym wyjściu w trybie programowania i korzysta z tej składni w szerokim zakresie.

Jlecour
źródło
5
Zastanawiałem się, dlaczego Elasticsearch na to pozwala. Oznacza to, że to zapytanie zliczające wszystkie dokumenty z ładunkiem do żądania GET curl -XGET 'http://localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } }' jest równoważne z włączeniem ładunku jako sourceparam: curl -XGET 'http://localhost:9200/_count?pretty&source=%7B%22query%22%3A%7B%22match_all%22%3A%7B%7D%7D%7D'
arun
40
Złożone zapytania mogą osiągnąć maksymalną długość nagłówka http.
s.Daniel
11
Czytałem dokumentację elasticsearch, która zaprowadziła mnie do tego pytania, ponieważ myślałem, że włączanie ciała za złą praktykę
PatrickWalker
1
Nie musi to być nawet złożone zapytanie. Nawet prosty zwój może zwrócić bardzo długi scroll_id (w klastrze z wieloma odłamkami), co spowoduje przekroczenie maksymalnej długości adresu URL, jeśli zostanie tam dodane.
Brent Hronik 27.07.16
11
Elasticsearch obsługuje to samo żądanie przy użyciu POST. Zdecydowali się zezwolić na ciało w GET, ponieważ uważali, że GET jest bardziej semantycznie poprawny niż POST, jeśli chodzi o zapytania do danych. Zabawne, że Elasticsearch jest tak często wymieniana w tym wątku. Nie użyłbym jednego przykładu (choć popularnego produktu) jako powodu do zastosowania się do tej praktyki.
OSD
32

To, co próbujesz osiągnąć, zostało zrobione przez długi czas przy użyciu znacznie popularniejszej metody, która nie polega na użyciu ładunku z GET.

Możesz po prostu zbudować swój konkretny mediaty wyszukiwania, lub jeśli chcesz być bardziej RESTful, użyj czegoś takiego jak OpenSearch i WYŚLIJ żądanie do identyfikatora URI, który polecił serwer, powiedz / szukaj. Serwer może następnie wygenerować wynik wyszukiwania lub zbudować końcowy identyfikator URI i przekierować za pomocą 303.

Ma to tę zaletę, że postępuje zgodnie z tradycyjną metodą PRG, pomaga buforować wyniki pośrednikom w pamięci podręcznej itp.

To powiedziawszy, URI są i tak kodowane dla wszystkiego, co nie jest ASCII, podobnie jak application / x-www-form-urlencoded i multipart / form-data. Zalecam użycie tego zamiast tworzenia kolejnego niestandardowego formatu JSON, jeśli masz zamiar obsługiwać scenariusze ReSTful.

SerialSeb
źródło
4
Możesz po prostu zbudować swój konkretny mediaty wyszukiwania. Czy możesz opracować?
Piotr Dobrogost
2
Mówiłem przez to, że możesz utworzyć typ nośnika o nazwie application / vnd.myCompany.search + json, który zawierałby szablon wyszukiwania, który ma zostać wydany przez klienta, a następnie klient mógłby wysłać go jako POST. Jak już podkreśliłem, istnieje już taki typ nośnika i nazywa się on OpenSearch, ponowne użycie istniejącego typu nośnika powinno zostać wybrane zamiast niestandardowej trasy, kiedy można wdrożyć scenariusz z istniejącymi standardami.
SerialSeb
14
To sprytne, ale zbyt złożone i nieefektywne. Teraz musisz wysłać POST ze swoimi kryteriami wyszukiwania, uzyskać URI jako odpowiedź z POST, a następnie wysłać GET z URI kryteriów wyszukiwania na serwer, aby uzyskać GET kryteria i odesłać wynik z powrotem do Ciebie. (Z wyjątkiem tego, że włączenie identyfikatora URI do identyfikatora URI jest technicznie niemożliwe, ponieważ nie można wysłać czegoś, co może mieć do 255 znaków w obrębie czegoś, co nie może mieć więcej niż 255 znaków - więc musisz użyć częściowego identyfikatora, a następnie serwer musi wiedzieć, jak rozwiązać URI dla kryteriów wyszukiwania POSTed.)
fijiaaron
29

Możesz albo wysłać GET z ciałem, albo wysłać POST i zrezygnować z RESTish religijności (nie jest tak źle, 5 lat temu był tylko jeden członek tej wiary - jego komentarze powyżej).

Nie są to również świetne decyzje, ale wysłanie treści GET może zapobiec problemom niektórych klientów - i niektórych serwerów.

Wykonanie testu POST może napotkać przeszkody w niektórych ramach RESTish.

Julian Reschke zasugerował powyżej, używając niestandardowego nagłówka HTTP, takiego jak „SZUKAJ”, co może być eleganckim rozwiązaniem, z tym wyjątkiem, że jest jeszcze mniej prawdopodobne, że będzie obsługiwane.

Najbardziej wydajne może być wyświetlenie listy klientów, którzy mogą i nie mogą wykonać każdego z powyższych.

Klienci, którzy nie mogą wysłać GET z ciałem (o czym wiem):

  • XmlHTTPRequest Fiddler

Klienci, którzy mogą wysłać GET z treścią:

  • większość przeglądarek

Serwery i biblioteki, które mogą pobrać ciało z GET:

  • Apacz
  • PHP

Serwery (i serwery proxy), które usuwają ciało z GET:

  • ?
fijiaaron
źródło
2
Squid 3.1.6 usuwa również elementy GET, gdy Content-Length ma wartość 0 lub nie jest ustawiony, i w przeciwnym razie odsyła
wymaganą
2
Skrzypek to zrobi, ale ostrzega cię.
toddmo
Czy mówisz, że SEARCHmetoda może się po drodze złamać? Jeśli pełnomocnicy nie rozumieją metody, oczekuje się, że ją przeprowadzą, tak jak jest, więc nie jestem zbyt pewien, dlaczego według ciebie cokolwiek by to zepsuło ...
Alexis Wilke,
22

Zadałem to pytanie IGF HTTP WG. Komentarz Roy'a Fieldinga (autora dokumentu http / 1.1 w 1998 r.) Był taki

„... implementacja zostałaby zepsuta, aby zrobić coś innego niż parsowanie i odrzucenie tego ciała, jeśli zostanie otrzymane”

RFC 7213 (HTTPbis) stwierdza:

„Ładunek w komunikacie żądania GET nie ma zdefiniowanej semantyki;”

Teraz wydaje się jasne, że intencją było, aby znaczenie semantyczne w treściach żądania GET było zabronione, co oznacza, że ​​treść żądania nie może być użyta do wpłynięcia na wynik.

Istnieją serwery proxy, które na pewno złamią twoją prośbę na różne sposoby, jeśli dodasz ciało do GET.

Podsumowując, nie rób tego.

Adrien
źródło
21

Który serwer to zignoruje? - fijiaaron 30 sierpnia 2012 o 21:27

Google na przykład ma gorsze wyniki niż ignorowanie, uzna to za błąd !

Wypróbuj sam z prostym netcat:

$ netcat www.google.com 80
GET / HTTP/1.1
Host: www.google.com
Content-length: 6

1234

(po zawartości 1234 następuje CR-LF, czyli w sumie 6 bajtów)

a otrzymasz:

HTTP/1.1 400 Bad Request
Server: GFE/2.0
(....)
Error 400 (Bad Request)
400. That’s an error.
Your client has issued a malformed or illegal request. That’s all we know.

Dostajesz także 400 Bad Request od Bing, Apple itp. ... które są obsługiwane przez AkamaiGhost.

Dlatego nie zalecałbym używania żądań GET z jednostką treści.

użytkownik941239
źródło
65
Ten przykład jest bezcelowy, ponieważ zwykle, gdy ludzie dodają treść do GETżądań, to dlatego, że ich własny serwer niestandardowy jest w stanie to obsłużyć. Pytanie brzmi zatem, czy inne „części ruchome” (przeglądarki, pamięci podręczne itp.) Będą działać poprawnie.
Pacerier 19.04.2016
6
Jest to złe żądanie, ponieważ twoja ładowność nie jest oczekiwana (lub rozsądna) dla GET konkretnego punktu końcowego - nie ma to nic wspólnego z użyciem GETw ogólnym przypadku. Losowy ładunek może POSTrównie łatwo zepsuć się i zwrócić to samo 400 Bad Request, jeśli zawartość nie jest w formacie, który ma sens w kontekście konkretnego żądania.
nobar
I to nie tylko na tym punkcie końcowym jako całości, ale raczej na tym konkretnym adresie URL .
Lawrence Dol
1
Jest to nieistotne, ponieważ jest to tylko implementacja serwera Google pod tym adresem URL. Pytanie nie ma więc sensu
Joel Duckworth,
dla mnie było to użyteczne, ponieważ próbowałem użyć funkcji bazy ogniowej z poleceniem get + body, a ten błąd może być bardzo tajemniczy i trudny do zrozumienia.
scrimau
19

Z RFC 2616, sekcja 4.3 , „Treść wiadomości”:

Serwer POWINIEN czytać i przekazywać treść wiadomości na każde żądanie; jeśli metoda żądania nie obejmuje zdefiniowanej semantyki dla ciała encji, wówczas treść komunikatu POWINNA zostać zignorowana podczas obsługi żądania.

Oznacza to, że serwery powinny zawsze odczytywać wszelkie udostępnione treści żądania z sieci (sprawdzać Długość treści lub odczytywać fragmenty itp.). Ponadto pełnomocnicy powinni przekazywać wszelkie otrzymane przez siebie takie organy wniosku. Następnie, jeśli RFC definiuje semantykę dla treści dla danej metody, serwer może faktycznie użyć treści żądania do wygenerowania odpowiedzi. Jeśli jednak RFC nie definiuje semantyki dla treści, serwer powinien ją zignorować.

Jest to zgodne z cytatem z Fielding powyżej.

Sekcja 9.3 , „GET”, opisuje semantykę metody GET i nie wspomina o treściach żądań. Dlatego serwer powinien zignorować wszelkie treści żądania otrzymane na żądanie GET.

izrik
źródło
Sekcja 9.5 , „POST”, również nie wspomina o ciałach żądań, więc ta logika jest błędna.
CarLuva
9
@CarLuva Sekcja POST mówi: „Metoda POST służy do żądania, aby serwer początkowy zaakceptował załączoną jednostkę ...” Sekcja treści encji mówi: „Treść encji jest uzyskiwana z treści wiadomości…” Sekcja POST wspomina o treści komunikatu, chociaż pośrednio przez odwołanie się do treści encji, która jest przenoszona przez treść komunikatu żądania POST.
frederickf
9

Według XMLHttpRequest nie jest poprawny. Od standardu :

4.5.6 send()Metoda

client . send([body = null])

Inicjuje żądanie. Opcjonalny argument zapewnia treść żądania. Argument jest ignorowany, jeśli metodą żądania jest GETlub HEAD.

Zgłasza InvalidStateErrorwyjątek, jeśli którykolwiek stan nie jest otwarty lub send()flaga jest ustawiona.

Metoda musi uruchamiać kroki:send(body)

  1. Jeśli stan nie jest otwarty , wyrzuć InvalidStateErrorwyjątek.
  2. Jeśli send()flaga jest ustawiona, wyrzuć InvalidStateErrorwyjątek.
  3. Jeśli metodą żądania jest GETlub HEAD, ustaw wartość body na null.
  4. Jeśli ciało jest puste, przejdź do następnego kroku.

Chociaż nie sądzę, że powinno tak być, ponieważ żądanie GET może wymagać dużej zawartości treści.

Jeśli więc polegasz na XMLHttpRequest przeglądarki, prawdopodobnie nie będzie działać.

Sprzedaż Rafael
źródło
1
odrzucone ze względu na fakt, że XMLHttpRequest jest implementacją. Może nie odzwierciedlać faktycznej specyfikacji, którą ma zaimplementować.
floum
10
Upoważnienie powyżej jest błędne, jeśli niektóre implementacje nie obsługują wysyłania treści z GET, może to być powód, aby tego nie robić, niezależnie od specyfikacji. Właściwie natknąłem się na ten konkretny problem w produkcie na wielu platformach, nad którym pracuję - tylko platforma korzystająca z XMLHttpRequest nie wysłała get.
pjcard,
8

Jeśli naprawdę chcesz wysłać buforowalną treść JSON / XML do aplikacji internetowej, jedynym rozsądnym miejscem na umieszczenie danych jest ciąg zapytania zakodowany za pomocą RFC4648: kodowanie Base 64 z URL i bezpiecznym alfabetem nazwy pliku . Oczywiście możesz po prostu urlencode JSON i umieścić jest w wartości parametru param URL, ale Base64 daje mniejszy wynik. Pamiętaj, że istnieją ograniczenia rozmiaru adresu URL, zobacz Jaka jest maksymalna długość adresu URL w różnych przeglądarkach? .

Możesz myśleć, że =znak dopełniania Base64 może być zły dla wartości param adresu URL, jednak wydaje się, że nie - patrz ta dyskusja: http://mail.python.org/pipermail/python-bugs-list/2007-Feburn/037195.html . Nie należy jednak umieszczać zakodowanych danych bez nazwy parametru, ponieważ zakodowany ciąg z dopełnianiem będzie interpretowany jako klucz parametru z pustą wartością. Użyłbym czegoś takiego ?_b64=<encodeddata>.

gertas
źródło
Myślę, że to całkiem zły pomysł :) Ale gdybym zrobił coś takiego, zamiast tego użyłbym niestandardowego nagłówka HTTP (i upewniam się, że zawsze wysyłam Vary: w odpowiedzi).
Evert
Złe lub nie, ale wykonalne :) Z danych w nagłówku istnieje podobny problem z rozmiarem danych, patrz stackoverflow.com/questions/686217/... . Jednak dzięki za wzmiankę o Varynagłówku nie wiedziałem, że to prawdziwy potencjał.
gertas
6

Nie radziłbym tego, jest to sprzeczne ze standardowymi praktykami i nie oferuje tyle w zamian. Chcesz zachować treść, a nie opcje.

chmura
źródło
5

Co z niezgodnymi nagłówkami zakodowanymi w base64? „SOMETHINGAPP-PARAMS: sdfSD45fdg45 / aS”

Ograniczenia długości hm. Czy nie potrafisz rozróżnić znaczeń w procedurze POST? Jeśli chcesz prostych parametrów, takich jak sortowanie, nie rozumiem, dlaczego byłby to problem. Myślę, że martwisz się pewnością.

Chaz
źródło
Możesz przesłać dowolne parametry z x-prefiksem, wszelkie ograniczenia długości nagłówków byłyby całkowicie ograniczeniem arbitralnym serwera.
Chris Marisic
4

Jestem zdenerwowany tym, że REST, ponieważ protokół nie obsługuje OOP, a Getmetoda jest dowodem. Jako rozwiązanie możesz serializować DTO do JSON, a następnie utworzyć ciąg zapytania. Po stronie serwera możesz przekształcić ciąg zapytania do DTO.

Spójrz na:

Podejście oparte na wiadomościach może pomóc w rozwiązaniu problemu Ograniczenie metody. Będziesz mógł wysłać dowolne DTO jak w treści żądania

Struktura serwisu internetowego Nelibur zapewnia funkcjonalność, z której można korzystać

var client = new JsonServiceClient(Settings.Default.ServiceAddress);
var request = new GetClientRequest
    {
        Id = new Guid("2217239b0e-b35b-4d32-95c7-5db43e2bd573")
    };
var response = client.Get<GetClientRequest, ClientResponse>(request);

as you can see, the GetClientRequest was encoded to the following query string

http://localhost/clients/GetWithResponse?type=GetClientRequest&data=%7B%22Id%22:%2217239b0e-b35b-4d32-95c7-5db43e2bd573%22%7D
GSerjo
źródło
8
Powinieneś po prostu użyć POST. Jeśli w adresie URL znajduje się nazwa metody, naruszasz podstawowy projekt odpoczynku. To jest RPC, użyj POST.
Evert
3
Nie sądzę, żeby to była wielka sprawa, mamy więcej problemów podczas programowania z RESTful url (tj. Zamówienia / 1). Co do mnie, coś jest nie tak z metodą Get, jest niezgodne z OOP. I kogo obchodzi, jak wygląda adres URL :) Ale dzięki podejściu opartemu na wiadomościach możemy stworzyć stabilny zdalny interfejs i jest to naprawdę ważne. PS to nie RPC, oparte na wiadomościach
GSerjo
3
Myślę, że brakuje ci całego sensu REST. Kiedy mówisz, komu zależy na tym, jak wygląda adres URL, REST bardzo go obchodzi. I dlaczego REST byłby kompatybilny z OOP?
shmish111,
Nie, nie widziałem tylko trochę dalej
GSerjo,
4

Na przykład działa z Curl, Apache i PHP.

Plik PHP:

<?php
echo $_SERVER['REQUEST_METHOD'] . PHP_EOL;
echo file_get_contents('php://input') . PHP_EOL;

Polecenie konsoli:

$ curl -X GET -H "Content-Type: application/json" -d '{"the": "body"}' 'http://localhost/test/get.php'

Wynik:

GET
{"the": "body"}
mnv
źródło
Zabawny eksperyment! PHP będzie czytać tylko $_POSTwtedy, gdy treść zostanie wysłana z żądaniem POST i application/x-www-form-urlencoded. Oznacza to, że ciało jest ignorowane w GETżądaniu. W tym przypadku: $_GETi $_POSTw tym momencie bardzo wprowadzają w błąd. Lepsze wykorzystaniephp://input
Martin Muzatko,
3

IMHO możesz po prostu wysłać JSONzakodowane (tj. encodeURIComponent) W URLten sposób, aby nie naruszać HTTPspecyfikacji i dostać się JSONna serwer.

EthraZa
źródło
28
tak, ale głównym problemem jest limit długości, jak sobie z tym poradzić?
Sebas
2

Masz listę opcji, które są znacznie lepsze niż używanie treści żądania z GET.

Załóżmy, że masz kategorie i elementy dla każdej kategorii. Oba mają być identyfikowane przez id („catid” / „itemid” na potrzeby tego przykładu). Chcesz sortować według innego parametru „sortuj” w określonym „porządku”. Chcesz przekazać parametry „sortby” i „porządek”:

Możesz:

  1. Użyj ciągów zapytań, np example.com/category/{catid}/item/{itemid}?sortby=itemname&order=asc
  2. Użyj mod_rewrite (lub podobnego) dla ścieżek: example.com/category/{catid}/item/{itemid}/{sortby}/{order}
  3. Użyj poszczególnych nagłówków HTTP przekazywanych z żądaniem
  4. Użyj innej metody, np. POST, aby pobrać zasób.

Wszystkie mają swoje wady, ale są znacznie lepsze niż używanie GET z ciałem.

Ksenonit
źródło
0

Nawet jeśli popularne narzędzie tego używa, jak często cytowano na tej stronie, myślę, że nadal jest to dość zły pomysł, ponieważ jest zbyt egzotyczny, mimo że nie zabrania tego specyfikacja.

Wiele pośrednich infrastruktur może po prostu odrzucić takie żądania.

Poprzez przykład zapomnieć o korzystaniu z niektórych dostępnych CDN przed swojej stronie internetowej, jak ten jeden :

Jeśli GETżądanie przeglądarki zawiera treść, CloudFront zwraca do przeglądarki kod stanu HTTP 403 (Zabroniony).

I tak, biblioteki klienta mogą również nie obsługiwać wysyłania takich żądań, jak opisano w tym komentarzu .

Frédéric
źródło
0

Korzystam z RestTemplate frameworka Spring w moim programie klienckim i po stronie serwera zdefiniowałem żądanie GET z treścią Json. Mój główny cel jest taki sam jak twój: gdy żądanie ma wiele parametrów, umieszczenie ich w ciele wydaje się bardziej uporządkowane niż umieszczenie ich w przedłużonym ciągu URI. Tak?

Ale niestety to nie działa! Po stronie serwera zgłoszono następujący wyjątek:

org.springframework.http.converter.HttpMessageNotReadableException: Brak wymaganej treści żądania ...

Ale jestem prawie pewien, że treść mojego kodu klienta została poprawnie dostarczona, więc co jest nie tak?

Prześledziłem metodę RestTemplate.exchange () i znalazłem:

// SimpleClientHttpRequestFactory.class
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
    ...
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
        ...
        if (!"POST".equals(httpMethod) && !"PUT".equals(httpMethod) && !"PATCH".equals(httpMethod) && !"DELETE".equals(httpMethod)) {
            connection.setDoOutput(false);
        } else {
            connection.setDoOutput(true);
        }
        ...
    }
}

// SimpleBufferingClientHttpRequest.class
final class SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttpRequest {
    ...
    protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
        ...
        if (this.connection.getDoOutput() && this.outputStreaming) {
            this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
        }

        this.connection.connect();
        if (this.connection.getDoOutput()) {
            FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
        } else {
            this.connection.getResponseCode();
        }
        ...
    }
}

Zauważ, że w metodzie executeInternal () argument wejściowy „bufferedOutput” zawiera treść komunikatu dostarczoną przez mój kod. Widziałem to przez debugger.

Jednak z powodu preparConnection () funkcja getDoOutput () w executeInternal () zawsze zwraca false, co z kolei powoduje całkowite zignorowanie bufferedOutput! Nie jest kopiowany do strumienia wyjściowego.

W związku z tym mój program serwera nie otrzymał treści wiadomości i zgłosił ten wyjątek.

To jest przykład RestTemplate frameworka Spring. Chodzi o to, że nawet jeśli treść komunikatu nie jest już zabroniona przez specyfikację HTTP, niektóre biblioteki lub struktury klienta lub serwera mogą nadal być zgodne ze starą specyfikacją i odrzucić treść wiadomości z żądania GET.

Zhou
źródło
Nie czytasz tutaj poprawnie specyfikacji lub komentarzy. Klienci i serwery upuszczenie treści żądania jest wewnątrz spec. Nie używaj treści żądania GET.
Evert
@ Wyjdź Nie przeczytałem komentarzy poprawnie, a ty nie? :) Jeśli przewiniesz w górę do odpowiedzi Paula Morgana (najlepiej trafiającej odpowiedzi) i dokładnie przeczytasz komentarze, znajdziesz to: „RFC2616 oznaczony jako„ specyfikacja HTTP / 1.1 ”jest już nieaktualny. W 2014 r. Został zastąpiony przez Dokumenty RFC 7230-7237. Cytując „treść komunikatu POWINIEN zostać zignorowany podczas obsługi żądania” zostało usunięte. Teraz jest to po prostu „Ramka komunikatu żądania jest niezależna od semantyki metody, nawet jeśli metoda nie definiuje żadnego zastosowania treści wiadomości ... ”
Zhou,
@Evert Co więcej, użyłem narzędzia testującego REST „pewni”, aby przetestować mój backend Spring-boot. Zarówno spokojny, jak i Spring-boot po stronie serwera zachował ciało Json dla żądania GET! Tylko RestTemplate Sping-framework upuszcza ciało z żądań GET, więc Spring-boot, resture zapewniony i RestTemplate, który jest / jest zły?
Zhou,
@Evert Ostatnia, ale nie dzierżawa, nie nakłaniałem ludzi do używania ciała w żądaniach GET, wręcz przeciwnie, sugerowałem NIE robienie tego poprzez analizę kodu źródłowego RestTemplate Sping-framework, więc dlaczego obniżyłeś głos mojego? odpowiedź?
Zhou,
Nie głosowałem za twoją odpowiedzią. Po prostu wyjaśniam, że każda implementacja HTTP odrzucająca żądanie GET jest wyspecyfikowana.
Evert