Jak wersjonować identyfikatory URI REST

111

Jaki jest najlepszy sposób na utworzenie wersji identyfikatorów URI REST? Obecnie mamy numer wersji w samym URI, tj.

http://example.com/users/v4/1234/

dla wersji 4 tej reprezentacji.

Czy wersja należy do queryString? to znaczy.

http://example.com/users/1234?version=4

A może wersjonowanie najlepiej przeprowadzić w inny sposób?

Mike Pone
źródło

Odpowiedzi:

34

Powiedziałbym, że uczynienie go częścią samego URI (opcja 1) jest najlepsze, ponieważ v4 identyfikuje inny zasób niż v3. Parametry zapytania, takie jak w drugiej opcji, najlepiej nadają się do przekazywania dodatkowych informacji (zapytania) związanych z żądaniem , a nie zasobem .

Zef Hemel
źródło
11
Pytanie brzmi, czy omawiamy inny ZASÓB? Albo inna reprezentacja tego zasobu? Czy REST rozróżnia reprezentację od zasobu?
Cheeso
1
@Cheeso - OP wskazuje, że jest to inna reprezentacja, a nie inny zasób, stąd moja odpowiedź.
Greg Beech
Na to odpowiedź udzielono bardziej szczegółowo wcześniej tutaj stackoverflow.com/q/389169/104261
Taras Alenin
+1 dla „Parametry zapytania, takie jak w drugiej opcji, najlepiej nadają się do przekazywania dodatkowych informacji (zapytań) związanych z żądaniem, a nie zasobu”
andy,
Dla różnych reprezentacji myślę, że powinieneś użyć nagłówków, takich jak „Akceptuj”, wtedy klient może określić serwerowi „Akceptuję tylko wersję 4”, a serwer może odpowiedzieć za pomocą tej reprezentacji. Jeśli akceptacja nie zostanie wysłana, dostarczana jest ostatnia wersja.
Carlos Verdes
190

Nie wersjonuj adresów URL, ponieważ ...

  • łamiesz permalinki
  • Zmiany adresów URL będą rozprzestrzeniać się przez interfejs jak choroba. Co robisz z reprezentacjami, które się nie zmieniły, ale wskazują na reprezentację, która się zmieniła? Jeśli zmienisz adres URL, zepsujesz starych klientów. Jeśli opuścisz adres URL, nowi klienci mogą nie działać.
  • Wersjonowanie typów nośników jest znacznie bardziej elastycznym rozwiązaniem.

Zakładając, że Twój zasób zwraca jakiś wariant pliku application / vnd.yourcompany.user + xml, wszystko, co musisz zrobić, to utworzyć obsługę nowej aplikacji / vnd.yourcompany.userV2 + xml i za pomocą magii negocjacji treści v1 i Klienci v2 mogą pokojowo współistnieć.

W interfejsie RESTful najbliższą umową jest definicja typów mediów, które są wymieniane między klientem a serwerem.

Adresy URL, których klient używa do interakcji z serwerem, powinny być dostarczone przez serwer osadzony we wcześniej pobranych reprezentacjach. Jedynym adresem URL, który musi być znany klientowi, jest główny adres URL interfejsu. Dodawanie numerów wersji do adresów URL ma wartość tylko wtedy, gdy tworzysz adresy URL na kliencie, czego nie należy robić za pomocą interfejsu RESTful.

Jeśli chcesz zmienić typy mediów, które zepsują istniejących klientów, utwórz nowy i zostaw swoje adresy URL w spokoju!

A dla tych czytelników, którzy obecnie mówią, że nie ma to sensu, jeśli używam application / xml i application / json jako typów mediów. Jak mamy je wersjonować? Ty nie jesteś. Te typy mediów są prawie bezużyteczne dla interfejsu RESTful, chyba że przeanalizujesz je za pomocą pobierania kodu, w którym to momencie wersjonowanie jest kwestią sporną.

Darrel Miller
źródło
66
Aby odnieść się do punktów. 1. nie zrywasz linków perma, ponieważ linki bezpośrednie prowadzą do konkretnej wersji 2. Jeśli wszystko jest wersjonowane, to nie jest problem. Stare adresy URL mogą nadal działać. Idealnie byłoby, gdyby adres URL w wersji 4 zwracał powiązanie z zasobem w wersji 3. 3. Być może
Mike Pone
10
Wyobraź sobie, że po uaktualnieniu przeglądarki internetowej do nowej wersji wszystkie ulubione z zakładek się zepsuły! Pamiętaj, że koncepcyjnie użytkownik zapisuje łącze do zasobu, a nie do wersji reprezentacji zasobu.
Darrel Miller,
11
@Gili Aby spełnić wymóg, aby interfejs REST API był samoopisowy, konieczne jest, aby nagłówek typu treści zawierał pełny opis semantyczny wiadomości. Innymi słowy, typ mediów to umowa dotycząca danych. Jeśli dostarczasz application / xml lub application / json, nie mówisz klientowi nic o tym, co jest zawarte w tym XML / Json. W momencie, w którym aplikacja kliencka osiągnie połączenie / Klient / Nazwa, tworzysz połączenie oparte na informacjach, których nie ma w wiadomości. Wyeliminowanie sprzężenia pozapasmowego ma kluczowe znaczenie dla osiągnięcia pełnej zgodności.
Darrel Miller
6
@Gili Klient nie powinien mieć wcześniejszej wiedzy na temat adresów URL interfejsu API innych niż główny adres URL. Nie należy wiązać formatów reprezentacji z określonymi adresami URL. Jeśli chodzi o wybór typów mediów, naprawdę musisz wybrać między konkretnym formatem, takim jak application / vnd.mycompany.myformat + xml, lub standardowym, takim jak XHtml, Atom, RDF itp.
Darrel Miller,
4
Czy ma sens umieszczanie wersji API w osobnym polu nagłówka? Na przykład: Zaakceptuj: application / com.example.myapp + json; wersja = 1.0
Erik
21

Ach, znowu zakładam mój stary zrzędliwy kapelusz.

Z perspektywy ReST nie ma to żadnego znaczenia. To nie jest kiełbasa.

Klient otrzymuje identyfikator URI, za którym chce podążać, i traktuje go jako nieprzezroczysty ciąg. Umieść w nim cokolwiek chcesz, klient nie ma żadnej wiedzy na temat czegoś takiego jak identyfikator wersji.

Klient wie, że może przetwarzać rodzaj mediów, a ja radzę postępować zgodnie z radą Darrela. Osobiście uważam również, że czterokrotna zmiana formatu używanego w spokojnej architekturze powinna przynieść ogromne, masywne sygnały ostrzegawcze, że robisz coś naprawdę złego i całkowicie pomijając potrzebę zaprojektowania typu nośnika pod kątem odporności na zmiany.

Tak czy inaczej, klient może przetworzyć tylko dokument w formacie, który jest dla niego zrozumiały, i skorzystać z zawartych w nim linków. Powinien wiedzieć o związkach linków (przejściach). Więc to, co znajduje się w identyfikatorze URI, jest całkowicie nieistotne.

Osobiście zagłosowałbym na http: // localhost / 3f3405d5-5984-4683-bf26-aca186d21c04

Całkowicie poprawny identyfikator, który uniemożliwi dalszemu deweloperowi klienta lub osobie dotykającej systemu pytanie, czy należy umieścić v4 na początku lub na końcu identyfikatora URI (i sugeruję, że z punktu widzenia serwera nie powinno się mieć 4 wersje, ale 4 typy nośników).

SerialSeb
źródło
Co się stanie, jeśli reprezentacja musi się znacznie zmienić i nie będzie kompatybilna wstecz?
Mike Pone
1
Projektując typ multimediów w sposób rozszerzalny, na przykład przy użyciu przestrzeni nazw i rozszerzalnego xsd lub istniejących formatów xml, takich jak atom, można temu zapobiec. Jeśli naprawdę musisz, wybierz inny typ mediów.
SerialSeb
1
Podoba mi się ta całkowicie poprawna odpowiedź, ale myślę, że proponowany URI jest bardziej do zademonstrowania sensu niż dla prawdziwego scenariusza, w którym chcesz, aby identyfikatory URI można było zhakować.
Dave Van den Eynde
10

NIE powinieneś umieszczać wersji w adresie URL, powinieneś umieścić wersję w Akceptuj nagłówek żądania - zobacz mój post w tym wątku:

Najlepsze praktyki dotyczące wersjonowania API?

Jeśli zaczniesz umieszczać wersje w adresie URL, otrzymasz głupie adresy URL, takie jak: http://company.com/api/v3.0/customer/123/v2.0/orders/4321/

Pojawia się też kilka innych problemów - patrz mój blog: http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

jeremyh
źródło
11
Przykro mi, ale nie sądzę, że trafisz na takie głupie adresy URL. Wiążesz numery wersji z określonym zasobem lub (co gorsza) z określoną reprezentacją. To byłoby głupie, IMO. Raczej wersjonujesz interfejs API, więc nigdy nie miałbyś więcej niż jednej wersji w identyfikatorze URI.
fool4jesus
3

Istnieją 4 różne podejścia do wersjonowania API:

  • Dodawanie wersji do ścieżki URI:

    http://example.com/api/v1/foo
    
    http://example.com/api/v2/foo
    

    W przypadku istotnej zmiany należy zwiększyć wersję, taką jak: v1, v2, v3 ...

    Możesz zaimplementować kontroler w swoim kodzie w następujący sposób:

    @RestController
    public class FooVersioningController {
    
    @GetMapping("v1/foo")
    public FooV1 fooV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping("v2/foo")
    public FooV2 fooV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Poproś o wersję parametru:

    http://example.com/api/v2/foo/param?version=1
    http://example.com/api/v2/foo/param?version=2
    

    Parametr wersji może być opcjonalny lub wymagany, w zależności od tego, jak chcesz używać interfejsu API.

    Implementacja może wyglądać podobnie do tego:

    @GetMapping(value = "/foo/param", params = "version=1")
    public FooV1 paramV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping(value = "/foo/param", params = "version=2")
    public FooV2 paramV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Przekazywanie niestandardowego nagłówka:

    http://localhost:8080/foo/produces
    

    Z nagłówkiem:

    headers[Accept=application/vnd.company.app-v1+json]
    

    lub:

    headers[Accept=application/vnd.company.app-v2+json]
    

    Największą zaletą tego schematu jest głównie semantyka: nie zaśmiecasz identyfikatora URI niczym, co ma związek z wersjonowaniem.

    Możliwa realizacja:

    @GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v1+json")
    public FooV1 producesV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v2+json")
    public FooV2 producesV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Zmiana nazw hostów lub korzystanie z bram API:

    Zasadniczo przenosisz interfejs API z jednej nazwy hosta na inną. Możesz nawet wywołać to budowanie nowego interfejsu API do tych samych zasobów.

    Możesz to również zrobić za pomocą bram interfejsu API.

Javier C.
źródło
2

Jeśli usługi REST wymagają uwierzytelnienia przed użyciem, możesz łatwo powiązać klucz / token API z wersją API i wykonać routing wewnętrznie. Aby skorzystać z nowej wersji interfejsu API, może być wymagany nowy klucz API połączony z tą wersją.

Niestety to rozwiązanie działa tylko w przypadku interfejsów API opartych na uwierzytelnianiu. Jednak zachowuje wersje z dala od identyfikatorów URI.

UberSteve
źródło
2

Chciałem stworzyć wersjonowane API i ten artykuł był bardzo przydatny:

http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http

Jest tam mała sekcja na temat „Chcę, aby mój interfejs API był wersjonowany”. Wydało mi się to proste i łatwe do zrozumienia. Istotą jest użycie pola Accept w nagłówku do przekazania informacji o wersji.

Asma Zubair
źródło
1

Dołączę wersję jako wartość opcjonalną na końcu identyfikatora URI. Może to być sufiks taki jak / V4 lub parametr zapytania, jak opisałeś. Możesz nawet przekierować / V4 do parametru zapytania, aby obsługiwać obie odmiany.

Paul Morgan
źródło
1

Jeśli używasz identyfikatorów URI do przechowywania wersji, numer wersji powinien znajdować się w identyfikatorze URI katalogu głównego interfejsu API, aby każdy identyfikator zasobu mógł go zawierać.

Technicznie rzecz biorąc, interfejs API REST nie jest przerywany przez zmiany adresu URL (wynik jednolitego ograniczenia interfejsu). Zepsuje się tylko wtedy, gdy powiązana semantyka (na przykład słownik RDF specyficzny dla API) zmienia się w sposób niezgodny wstecz (rzadko). Obecnie wielu ppl nie używa linków do nawigacji (ograniczenie HATEOAS) i słowników do opisywania swoich odpowiedzi REST (samoopisowe ograniczenie wiadomości), dlatego ich klienci przerywają.

Niestandardowe typy MIME i wersjonowanie typów MIME nie pomagają, ponieważ umieszczenie powiązanych metadanych i struktury reprezentacji w krótkim ciągu nie działa. Ofc. metadane i struktura będą się często zmieniać, więc numer wersji też ...

Zatem, aby odpowiedzieć na twoje pytanie, najlepszym sposobem na dodawanie adnotacji do twoich żądań i odpowiedzi za pomocą słowników ( Hydra , połączone dane ) i zapomnij o wersjonowaniu lub używaj go tylko przez niekompatybilne wstecznie zmiany słownictwa (na przykład, jeśli chcesz zastąpić słownik innym).

inf3rno
źródło
0

Głosuję za zrobieniem tego w typie mime, ale nie w adresie URL. Ale powód nie jest taki sam, jak inni faceci.

Myślę, że adres URL powinien być unikalny (z wyjątkiem tych przekierowań) do lokalizowania unikalnego zasobu. Tak więc, jeśli przyjąć /v2.0w URL, dlaczego nie jest /ver2.0lub /v2/czy /v2.0.0? Albo nawet -alphai -beta? (wtedy całkowicie staje się pojęciem semver )

Tak więc wersja typu mime jest bardziej akceptowalna niż adres URL.

Yarco
źródło