Projektując usługę internetową zgodną z REST przy użyciu HATEOAS, jakie są zalety i wady wyświetlania linku jako pełnego adresu URL („ http: // serwer: port / aplikacja / klienci / 1234 ”), a nie tylko ścieżki („/ aplikacja / klientów / 1234 ")?
Projektując usługę internetową zgodną z REST przy użyciu HATEOAS, jakie są zalety i wady wyświetlania linku jako pełnego adresu URL („ http: // serwer: port / aplikacja / klienci / 1234 ”), a nie tylko ścieżki („/ aplikacja / klientów / 1234 ")?
Odpowiedzi:
Kiedy ludzie mówią „względny URI”, występuje subtelna dwuznaczność pojęciowa.
Zgodnie z definicją RFC3986 , ogólny identyfikator URI zawiera:
URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
hier-part = "//" authority path-abempty
/ path-absolute
/ path-rootless
/ path-empty
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
Problem polega na tym, że gdy pominie się schemat i autorytet, sama część „ścieżki” może być ścieżką bezwzględną (zaczyna się od /
) lub ścieżką względną „bez korzeni”. Przykłady:
"http://example.com:8042/over/there?name=ferret"
/over/there
here
lub ./here
lub ../here
lub itd.Tak więc, jeśli pytanie brzmiało „czy serwer powinien generować ścieżkę względną w odpowiedzi spokojnej”, odpowiedź brzmi „Nie”, a szczegółowy powód jest dostępny tutaj . Myślę, że większość ludzi (łącznie ze mną) sprzeciwiających się „względnemu URI” jest w rzeczywistości przeciwna „względnej ścieżce”.
W praktyce większość frameworków MVC po stronie serwera może z łatwością generować względne URI ze ścieżką bezwzględną, taką jak /absolute/path/to/the/controller
, i pojawia się pytanie „czy implementacja serwera powinna poprzedzać scheme://hostname:port
ścieżkę bezwzględną przed prefiksem a ”. Jak pytanie OP. Nie jestem pewien co do tego.
Z jednej strony nadal uważam, że serwer zwraca pełny identyfikator URI. Jednak serwer nigdy niehostname:port
powinien zakodować rzeczy w kodzie źródłowym w ten sposób (w przeciwnym razie wolałbym raczej wrócić do względnego URI z bezwzględną ścieżką). Rozwiązanie polega na tym, że po stronie serwera zawsze uzyskuje się ten prefiks z nagłówka „Host” żądania HTTP. Nie jestem jednak pewien, czy to działa w każdej sytuacji.
Z drugiej strony łączenie http://example.com:8042
ścieżki bezwzględnej i ścieżki absolutnej nie wydaje się kłopotliwe dla klienta . Przecież klient zna już ten schemat i nazwę domeny, kiedy wysyła żądanie do serwera, prawda?
Podsumowując, powiedziałbym, że zalecam użycie bezwzględnego URI, prawdopodobnie powrót do względnego URI ze ścieżką bezwzględną, nigdy nie używaj ścieżki względnej .
To zależy od tego, kto pisze kod klienta. Jeśli piszesz o kliencie i serwerze, nie ma to większego znaczenia. Będziesz cierpieć ból związany z budowaniem adresów URL na kliencie lub na serwerze.
Jeśli jednak budujesz serwer i oczekujesz, że inni ludzie będą pisać kod klienta, pokochają Cię znacznie bardziej, jeśli podasz pełne identyfikatory URI. Rozwiązywanie względnych identyfikatorów URI może być nieco trudne. Po pierwsze, sposób ich rozwiązania zależy od zwróconego typu nośnika. Html ma tag podstawowy, Xml może mieć tagi xml: base w każdym zagnieżdżonym elemencie, kanały Atom mogą mieć bazę w źródle i inną podstawę w treści. Jeśli nie podasz swojemu klientowi wyraźnych informacji o podstawowym URI, będzie on musiał uzyskać podstawowy URI z URI żądania lub może z nagłówka Content-Location! I uważaj na to końcowe ukośnik. Podstawowy identyfikator URI jest określany przez ignorowanie wszystkich znaków po prawej stronie ostatniego ukośnika. Oznacza to, że końcowy ukośnik jest teraz bardzo istotny podczas rozwiązywania względnych identyfikatorów URI.
Jedyną inną kwestią, która wymaga małej wzmianki, jest rozmiar dokumentu. Jeśli zwracasz dużą listę elementów, w których każdy element może mieć wiele linków, użycie bezwzględnych adresów URL może dodać znaczną ilość bajtów do jednostki, jeśli nie skompresujesz jednostki. Jest to problem z perfekcją i musisz zdecydować, czy jest istotny w każdym przypadku z osobna.
W miarę skalowania aplikacji możesz chcieć wykonać równoważenie obciążenia, przełączanie awaryjne itp. Jeśli zwrócisz bezwzględne identyfikatory URI, aplikacje po stronie klienta będą podążać za zmieniającą się konfiguracją serwerów.
/xxx/yyy...
), A nie jako w pełni kwalifikowany identyfikator URI (np http://api.example.com/xxx/yyy...
.).
Korzystając z trychotomii RayLou, moja organizacja zdecydowała się na faworyzowanie (2). Głównym powodem jest unikanie ataków XSS (Cross-Site Scripting). Problem polega na tym, że jeśli atakujący może wstawić swój własny adres URL do odpowiedzi wracającej z serwera, wówczas kolejne żądania użytkowników (takie jak żądanie uwierzytelnienia z nazwą użytkownika i hasłem) mogą być przekazywane na własny serwer atakującego *.
Niektórzy poruszyli kwestię możliwości przekierowywania żądań do innych serwerów w celu równoważenia obciążenia, ale (chociaż nie jest to moja specjalizacja), założyłbym się, że istnieją lepsze sposoby włączenia równoważenia obciążenia bez konieczności jawnego przekierowywania klientów do innych zastępy niebieskie.
* proszę dać mi znać, jeśli są jakieś błędy w tej linii rozumowania. Celem oczywiście nie jest zapobieganie wszystkim atakom, ale przynajmniej jednej drodze ataku.
Należy zawsze używać pełnego adresu URL. Działa jako unikalny identyfikator zasobu, ponieważ wszystkie adresy URL muszą być unikalne.
Twierdziłbym również, że powinieneś być konsekwentny. Ponieważ nagłówek HTTP lokalizacji oczekuje pełnego adresu URL na podstawie specyfikacji HTTP, pełny adres URL jest przesyłany z powrotem w nagłówku lokalizacji do klienta, gdy tworzony jest nowy zasób. Byłoby dziwne podanie pełnego adresu URL w nagłówku lokalizacji, a następnie względnych identyfikatorów URI w linkach w treści odpowiedzi.
Location
daje przykład nagłówka specyfikacji - absolutny URI, który nie zawiera schematu URI ani lokalizacji sieciowej serwera. Chociaż linki i identyfikatory są często ze sobą powiązane, nie są tym samym - pierwszy ma kontekst, a drugi nie.
Ważną kwestią w przypadku dużych wyników API jest dodatkowe obciążenie sieci związane z wielokrotnym włączaniem pełnego identyfikatora URI. Wierz lub nie, ale gzip nie rozwiązuje całkowicie tego problemu (nie wiem dlaczego). Byliśmy zszokowani tym, ile miejsca zajmował pełny identyfikator URI, gdy wynik zawierał setki linków.
Jedną z wad używania bezwzględnych identyfikatorów URI jest to, że nie można proxy interfejsu API.
Cofnij to ... nieprawda. Powinieneś wybrać pełny adres URL, w tym domenę.
Jeśli chodzi o zalety, widzę redukcję bajtów do przesłania kosztem dodatkowej obsługi wymaganej przez klienta dla ścieżki (bezwzględnej). Jeśli jesteś zdesperowany, aby zapisać każdy bajt, nawet po wypróbowaniu kodowania treści jako gzip, właściwego użycia nagłówków pamięci podręcznej, użycia etagów i warunkowych żądań na kliencie, może to być ostatecznie konieczne, ale spodziewam się znacznie wyższych zwrotów twoje wysiłki były gdzie indziej.
Jeśli chodzi o wady, widzę utratę kontroli nad sposobem kierowania przepływem klientów między zasobami w przyszłości (równoważenie obciążenia, testy A / B, ...) i uznałbym to za złą praktykę w zakresie zarządzania siecią API. Podany adres URL nie jest już w zasadzie nieprzejrzysty dla klienta (patrz Aksjomaty architektury sieciowej Tima Bernersa-Lee dotyczące nieprzezroczystości identyfikatora URI ). Ostatecznie stajesz się odpowiedzialny za zadowolenie klientów z kreatywnego wykorzystania interfejsu API, nawet jeśli dotyczy to tylko struktury przestrzeni adresów URL. Jeśli kiedykolwiek będziesz musiał zezwolić na jawnie zdefiniowaną modyfikację adresu URL, rozważ użycie szablonów URI, które są używane w języku aplikacji hipertekstu .