Konwencja REST URI - pojedyncza lub mnoga nazwa zasobu podczas jego tworzenia


529

Jestem nowy w REST i zauważyłem, że w niektórych usługach RESTful używają one różnych identyfikatorów URI zasobów do aktualizacji / pobierania / usuwania i tworzenia. Jak na przykład

  • Twórz - używając / resources metodą POST (obserwuj liczbę mnogą) w niektórych miejscach używając / resource (liczba pojedyncza)
  • Aktualizacja - za pomocą / resource / 123 z metodą PUT
  • Pobierz - za pomocą / resource / 123 z metodą GET

Jestem trochę zdezorientowany tą konwencją nazewnictwa URI. Czego powinniśmy używać w liczbie mnogiej lub pojedynczej do tworzenia zasobów? Jakie powinny być kryteria przy podejmowaniu takiej decyzji?


9
Po tym temacie zebrałem kilka przykładów znanych interfejsów API REST w artykule: inmensosofa.blogspot.com/2011/10/… .
jjmontes

3
Doszedłem do wniosku po przeczytaniu wszystkich poniższych odpowiedzi: Zawsze używaj liczby pojedynczej, ponieważ (a) jest spójna, (b) odwzorowuje bezpośrednio na pojedyncze nazwy klas i tabel, (c) niektóre rzeczowniki w liczbie mnogiej są nieregularne (nieprzewidywalne) w języku angielskim
Will Sheppard

Zobacz tę odpowiedź, aby znaleźć link do konwencji nazewnictwa pojedynczych tabel, i jest jeszcze jeden artykuł, który wspomina o tym właśnie problemie Dylemat programisty Rest API Developers - dziękuję @Sorter
Will Sheppard

Odpowiedzi:


280

Założeniem użycia /resourcesjest to, że reprezentuje on „wszystkie” zasoby. Jeśli to zrobisz GET /resources, prawdopodobnie zwrócisz całą kolekcję. Przesyłając do /resources, dodajesz do kolekcji.

Jednak poszczególne zasoby są dostępne w / zasób. Jeśli to zrobisz GET /resource, prawdopodobnie popełnisz błąd, ponieważ to żądanie nie ma żadnego sensu, a /resource/123ma doskonały sens.

Korzystanie /resourcezamiast /resourcesjest podobny do tego, jak chcesz to zrobić, jeśli pracowaliśmy z, powiedzmy, systemu plików i kolekcji plików i /resourcejest „katalog” z poszczególnymi 123, 456pliki w nim.

Żadna z tych dróg nie jest dobra ani zła, wybierz to, co lubisz najbardziej.


55
Świetna odpowiedź! Ale „domyślne” katalogi w systemie Windows mają nazwy w liczbie mnogiej . Jak „Pliki programów”, „Użytkownicy”, „Dokumenty”, „Filmy wideo” itp. Również znacznie częściej napotykałem liczby mnogie w adresach URL witryn.
Dmitry Gonchar,

50
Konwencja defacto prawie wszyscy ludzie i dostępne interfejsy API utrzymują ją w liczbie mnogiej przez cały czas. Identyfikatory określają JEDEN zasób samochodów / identyfikator
PositiveGuy

205
„Żadna droga nie jest dobra ani zła, wybierz to, co lubisz najbardziej.” Ach, słynna linia, którą tak często słyszę i męczę się słysząc od ludzi. Konwencje mają znaczenie i POWINNY być konstruktywnie omawiane przez społeczność, to tam powstają lepsze rozwiązania i dobre praktyki. Gdy używasz zarówno liczby mnogiej, jak i pojedynczej dla nazw zasobów w identyfikatorach URI, komplikuje to kod i interfejs API, ponieważ użytkownik i kod stojący za interfejsem API muszą uwzględnić to w trasach i logice, aby rozróżnić pojedyncze i mnogie, a jeśli po prostu trzymasz z liczbą mnogą cały czas nie masz problemów.
PositiveGuy,

30
@TomaszPluskiewicz Masz całkowitą rację, że klienci nie dbają. Jako programistów my powinniśmy dbać - i że zgadzam się z komentarzem WTF, że konstruktywne dyskusje na temat konwencji są cenne.
Travis D

12
Więc ktoś może po prostu odpowiedzieć jednym słowem i zaakceptować, aby nie musiałem czytać tego wszystkiego (ponownie).
Ben George

623

Dla mnie lepiej jest mieć schemat, który można odwzorować bezpośrednio na kod (łatwy do zautomatyzowania), głównie dlatego, że kod będzie tym, co będzie na obu końcach.

GET  /orders          <---> orders 
POST /orders          <---> orders.push(data)
GET  /orders/1        <---> orders[1]
PUT  /orders/1        <---> orders[1] = data
GET  /orders/1/lines  <---> orders[1].lines
POST /orders/1/lines  <---> orders[1].lines.push(data) 

22
Trudność lub łatwość tego wynika z nieprzestrzegania HATEOS. Nie powinno mieć znaczenia, czy to liczba mnoga, czy liczba pojedyncza, czy cokolwiek innego. Powinieneś uszanować kody Uri wysłane z serwera, a nie „gromadzić” swoich kodów na kliencie. Następnie musisz wykonać 0 mapowań dla swojego kodu.
Richard

7
@richard Klient nadal musi wykonać mapowanie. W systemie HATEOS musieliby odwzorować nazwę, która reprezentuje relację (rel) do konstrukcji identyfikatora URI. Rel, metoda (czasownik) i Content-Type tworzą następnie nośnik zasobów. Nie wyklucza to potrzeby dobrego projektu URI. Mimo że klient może dawać pierwszeństwo nazwie rel, programiści API nadal potrzebują dobrego, czytelnego dla człowieka standardu budowy URI.

4
To jest, moim zdaniem, lepsza odpowiedź. Tyle że zawsze wolałem używać liczby pojedynczej niż mnogiej. User.getList (), User.getById, User.delete itp.
Monk Wschodni

3
Lubię prostotę. Zaletą mapowania jest także niezwykle łatwa do napisania dokumentacja i testy tras.
delos

5
To ma dla mnie sens. Jesteśmy jednak sklepem baz danych, co oznacza, że ​​generujemy jednostki kodu i API z naszego schematu bazy danych. Standardy baz danych zwykle opowiadają się za pojedynczymi nazwami tabel, więc idziemy z tym, ale nadal pod tą samą logiką jak ta odpowiedź.
André C. Andersen

274

Nie widzę sensu w robieniu tego i myślę, że nie jest to najlepszy projekt URI. Jako użytkownik usługi RESTful spodziewałbym się, że zasób listy będzie miał tę samą nazwę, bez względu na to, czy uzyskam dostęp do listy, czy do określonego zasobu „na” liście. Powinieneś używać tych samych identyfikatorów bez względu na to, czy chcesz użyć zasobu listy, czy określonego zasobu.


69
Według mnie to najlepsza odpowiedź. Doceniam to, że projektantom API podoba się poprawność językowa powiedzenia „pobierz zasób nr 123”, ale to dodatkowe kłopoty z kodowaniem podczas pisania klientów API, a także dokumentacji pomocy. (GET / api / people vs. GET / api / person / 123? Euuuchh.) .... zamiast myśleć o tym jak „zdobądź zasób nr 123”, wypowiedz to sobie w głowie jak „weź ze zbioru zasobów, które pasuje do 123 ".
Warren Rumak

5
W rozróżnianiu liczby mnogiej / pojedynczej nie chodzi o poprawność językową, ale o skalę. / workers / 12 czyta mi jako podzbiór zasobów pracowników o identyfikatorze „12” (może to znaczyć wszystko, na przykład zapisane zapytanie wyszukiwania ostatnio zwolnionych pracowników). Jeśli przeczytasz powyższe jako pracownik o identyfikatorze „12”, w jaki sposób reprezentowałbyś ten podzbiór? Jedyną opcją jest uczynienie bardziej złożonego rudy URI rozróżniającym kolekcje zawierające obiekty z samych obiektów (tj. Liczba pojedyncza vs. mnoga).
Erik,

9
Wybór / workers / 12 do reprezentowania zapytania dotyczącego ostatnio zwolnionych pracowników (lub dowolnego podzbioru) byłby złym projektem. Jeśli chcesz reprezentować podzbiory dowolnego rodzaju, sugeruję wprowadzenie ich jako zasobów (o odpowiednich nazwach).
Jan Deinhard

3
Nie ma to nic wspólnego ze zrozumieniem dla klientów. Chodzi o adresowanie różnych rzeczy za pomocą różnych adresów URL. I możliwość reagowania na wszystkie metody HTTP bez konfliktu. Możesz mieć zasób będący zbiorem przedmiotów oraz zasób, który reprezentuje sam przedmiot. Zależy mi na tym, że zasobem kolekcji może być example.org/166316e2-e1 i jeden konkretny element w tej kolekcji example.org/20d68348-ccc-001c4200de . Klient nie powinien konstruować adresów URL (co oczywiście nie jest skalowane, nie jest to usługa RESTful i do tego służą typy relacji łącza).
Erik,

4
Jeśli nie uważasz, że arbitralne adresy URL są ładne, możesz zidentyfikować zasoby kolekcji o liczbie mnogiej i pojedynczy element o pojedynczej nazwie. Jeśli nie lubisz angielskich adresów URL, a twój język naturalny nie obsługuje tego sposobu notacji pojedynczej / mnogiej, użyj czegoś innego, aby zdefiniować go w preferowanym języku. Przypuszczam, że wszystkie języki pozwalają ci w jakiś sposób odróżnić '/ the-collection-of- bla / 2321 „versus” bla / 61 ”na piśmie. I każdy z tych dwóch różnych zasobów reprezentuje zupełnie inne wyniki podczas wysyłania GET / PUT / DELETE / POST / PATCH i innych.
Erik,

77

Liczba mnoga

  • Proste - wszystkie adresy URL zaczynają się od tego samego prefiksu
  • Logiczne - orders/pobiera indeksowaną listę zamówień.
  • Standard - najczęściej przyjęty standard, a po nim przeważająca większość publicznych i prywatnych interfejsów API.

Na przykład:

GET /resources - zwraca listę elementów zasobów

POST /resources - tworzy jeden lub wiele elementów zasobów

PUT /resources - aktualizuje jeden lub wiele elementów zasobów

PATCH /resources - częściowo aktualizuje jeden lub wiele elementów zasobów

DELETE /resources - usuwa wszystkie elementy zasobów

A dla pojedynczych elementów zasobów:

GET /resources/:id- zwraca określony element zasobu na podstawie :idparametru

POST /resources/:id - tworzy jeden element zasobu o określonym identyfikatorze (wymaga weryfikacji)

PUT /resources/:id - aktualizuje określony element zasobu

PATCH /resources/:id - częściowo aktualizuje określony element zasobu

DELETE /resources/:id - usuwa określony element zasobu

Zwolennicy liczby pojedynczej pomyśl o tym w ten sposób: Czy poprosiłbyś kogoś o coś orderi oczekiwałbyś jednej rzeczy lub listy rzeczy? Dlaczego więc miałbyś oczekiwać, że usługa zwróci listę rzeczy podczas pisania /order?


10
Liczba pojedyncza : W przypadku, gdy częścią twojego systemu jest tylko jeden obiekt (0-1, istnieje lub nie), np. Users / 1 / avatar, możesz użyć formy pojedynczej do oznaczenia tego pojedynczego obiektu (np. Awatara) - bardziej szczegółowy przykład tutaj: stackoverflow .pl / a / 38296217/860099 . BTW - bardzo fajna odpowiedź :)
Kamil Kiełczewski,

Co z mapowaniem na nazwy klas i tabel, które powinny być w liczbie pojedynczej? (patrz inna odpowiedź )
Czy Sheppard

@WillSheppard - Nazwy klas są najlepsze w liczbie pojedynczej, a nazwy tabel najlepiej w liczbie mnogiej. Na przykład Orderto dobra nazwa dla klasy, która zajmuje się pojedynczymi instancjami obiektów odnoszącymi się do jednego rzędu. OrderListto nazwa klasy, która zajmuje się wieloma Orderinstancjami. Orders Tableto dobra nazwa tabeli bazy danych zawierającej wiele zamówień.
Eric Knudtson,

Chcę uzyskać / zamówienia, ale chcę tylko / 1
Jim Smith

@ Jim-Smith, dlaczego nie poprosisz / 1 z kolekcji użytkowników z GET / users / 1?
Rohmer

49

Pojedynczy

Wygoda Rzeczy mogą mieć nieregularne nazwy w liczbie mnogiej. Czasami nie mają. Ale pojedyncze nazwy są zawsze dostępne.

np. CustomerAddress nad CustomerAddresses

Rozważ ten powiązany zasób.

Jest /order/12/orderdetail/12to bardziej czytelne i logiczne niż /orders/12/orderdetails/4.

Tabele bazy danych

Zasób reprezentuje byt taki jak tabela bazy danych. Powinien mieć logiczną pojedynczą nazwę. Oto odpowiedź na nazwy tabel.

Mapowanie klas

Zajęcia są zawsze pojedyncze. Narzędzia ORM generują tabele o takich samych nazwach jak nazwy klas. Ponieważ coraz więcej narzędzi jest używanych, pojedyncze nazwy stają się standardem.

Przeczytaj więcej o Dylemacie programisty interfejsu API REST


39
Zawsze są tam osobliwe nazwiska /clothe/12/trouser/34 :)
Gert Arnold

18
@GertArnold słowo clothejest czasownikiem. Pozostałe interfejsy API zwykle trzymają się rzeczowników, gdy mówią o zasobach, i używają czasowników podczas opisywania akcji. Forma pojedyncza jest clout, ale jest archaiczna i prawdopodobnie lepiej by ją zastąpić garment.
Steve Buzonas

@ SteveBuzonas A do spodni i okularów przeciwsłonecznych?
Koray Tugay

32

Podczas gdy najbardziej rozpowszechnioną praktyką są RESTful apis, w których używa się liczby mnogiej, np. /api/resources/123Jest jeden szczególny przypadek, w którym uważam, że użycie nazwy pojedynczej jest bardziej odpowiednie / wyraziste niż liczby mnogie. Jest tak w przypadku relacji jeden do jednego. W szczególności, jeśli element docelowy jest obiektem wartości (w paradygmacie projektowania opartego na domenie).

Załóżmy, że każdy zasób ma jeden do jednego, accessLogktóry można by modelować jako obiekt wartości, tj. Nie byt, dlatego nie ma identyfikatora. Można to wyrazić jako /api/resources/123/accessLog. Zwykłe czasowniki (POST, PUT, DELETE, GET) odpowiednio wyrażają zamiar, a także fakt, że związek jest rzeczywiście jeden do jednego.


4
Niezła próba. Ale byłoby lepiej jako „accessLogEntries”. :-)
Tom Russell

8
@TomRussell dlaczego? Konsekwencje tego są ważne. Rozumiem, dlaczego miałbyś używać liczby mnogiej nawet wtedy, gdy uzyskujesz dostęp do zasobu za pomocą identyfikatora, ale w przypadku relacji jeden do jednego lub jeden do jednego jest to dość mylące. Zastanów się nad interfejsem API zarządzającym pracownikami w firmie z wieloma lokalizacjami. Każdy członek personelu pracuje w jednym miejscu. GET /users/123/locationpowinien pobrać lokalizację, w której pracuje użytkownik. Czy tak GET /users/123/locationsnaprawdę konsument nie wprowadza w błąd?
Carrie Kendall

1
@CarrieKendall Rozumiem twój punkt widzenia. Ponieważ accessLogjest modelowany jako atrybut lub wartość, a nie jako jednostka, powinien być liczbą pojedynczą. Jeśli masz zbyt dużo inżynierii, wpis w dzienniku byłby bytem i miałbyś /api/accessLogEntries?resource=123.
Tom Russell

Zgadzam się, choć myślę, że łamie konwencję mnożenia wszystkich rzeczy. To trudne. Dla mnie ważne jest, aby interfejs API był prosty, tzn. Dokumentacja powinna uzupełniać już i tak prostą implementację.
Carrie Kendall,

Jestem bardziej programistą niż osobą zajmującą się systemami lub bazami danych, więc lubię API, które opowiada historię, a nie przestrzega konwencji. Implikacje dla zautomatyzowanej dokumentacji są jednak prawdziwe.
Tom Russell

26

Dlaczego nie podążać za powszechnym trendem nazw tabel w bazie danych, w którym ogólnie przyjmuje się formę pojedynczą? Byłem tam, zrobiłem to - wykorzystajmy ponownie.

Dylemat nazywania tabel: nazwy w liczbie pojedynczej i mnogiej


8
Das Auto jest znacznie lepszy niż Die Autos. Również angielskie konwencje liczby mnogiej nie są spójne.
FlavorScape

7
Przestrzeń nazw zasobów jest kwestią semantyki, a nie implementacji. Zatem stosowanie analogii tabel DB nie jest zbyt szczęśliwe. Również podczas pracy z DB-em manipulujesz tylko tabelami, choć oczywiście możesz wpływać na zawartość (wiersze), ale w REST nie ma ograniczeń, aby bezpośrednio manipulować pojedynczym zasobem.
arpadf

3
Myślę, że jest to dobra analogia, ale ważniejsza niż decyzja, czy pójść w liczbie pojedynczej czy mnogiej, jest spójna z tym, co wybierzesz. Nie zamierzasz wstawiać do użytkowników, a następnie wybierać z użytkownika. Ta sama reguła powinna mieć zastosowanie do zasobów REST - nie zmieniaj ich nazw w zależności od tego, co robisz.
Todd Menier

3
To nie tylko nazwy tabel, ale także porównywalne z nazwami klas w OO (moja klasa nazywa się Klientem, a nie Klientem).
bytedev

W tym przypadku semantyczny jest zbyt ważny, aby po prostu zaakceptować „już zdefiniowane” trendy
Cattani Simone

19

Jestem zaskoczony, widząc, że tylu ludzi wskoczyłoby na mnogą rzeczownikową modę. Czy podczas implementacji konwersji liczby pojedynczej na liczbę mnogą zajmujesz się nieregularnymi rzeczownikami w liczbie mnogiej? Czy lubisz ból?

Zobacz http://web2.uvcs.uvic.ca/elc/studyzone/330/grammar/irrplu.htm

Istnieje wiele rodzajów nieregularnej liczby mnogiej, ale są one najczęściej:

Rodzaj rzeczownika Formowanie liczby mnogiej Przykład

Ends with -fe   Change f to v then Add -s   
    knife   knives 
    life   lives 
    wife   wives
Ends with -f    Change f to v then Add -es  
    half   halves 
    wolf   wolves
    loaf   loaves
Ends with -o    Add -es 
    potato   potatoes
    tomato   tomatoes
    volcano   volcanoes
Ends with -us   Change -us to -i    
    cactus   cacti
    nucleus   nuclei
    focus   foci
Ends with -is   Change -is to -es   
    analysis   analyses
    crisis   crises
    thesis   theses
Ends with -on   Change -on to -a    
    phenomenon   phenomena
    criterion   criteria
ALL KINDS   Change the vowel or Change the word or Add a different ending   
     man   men
     foot   feet
     child   children
     person   people
     tooth   teeth
     mouse   mice
 Unchanging Singular and plural are the same    
     sheep deer fish (sometimes)

5
Nie rozumiem tutaj troski. Nie powinniśmy programowo zmieniać liczby pojedynczej na liczbę mnogą. Większość powyższych form liczby mnogiej jest dobrze znana i nie powinna stanowić problemu. Jeśli ktoś ma słabą znajomość języka angielskiego, nieprawidłowo przeliteruje dowolną część zmiennej. Ponadto, kierując się logiką, czy zalecacie również używanie pojedynczych formularzy do odwoływania się do kolekcji w kodzie źródłowym?
kishor borate

1
Istnieją angielskie słowa, które są nieregularne do tego stopnia, że ​​często stanowią problem, nawet w obrębie anglosfery i są to powszechnie używane terminy, takie jak indeks / indeksy / indeksy, wierzchołek / wierzchołki / wierzchołki, macierz / macierze / macierze, promień / promienie / promienie itp. Nie widzę sensu w tworzeniu liczby mnogiej ścieżek REST, ponieważ jeśli wszystkie są konsekwentnie pojedyncze, jest to dla wszystkich bardziej oczywiste.
cholera

@ kishorborate, Używanie liczby mnogiej jako identyfikatora URI jest bardziej podatne na błędy, nawet dla rodzimych użytkowników języka angielskiego. Jak wskazuje cholera, liczby mnogie, takie jak indeks / indeksy / indeksy, wprowadzają więcej problemów. I są niepoliczalne rzeczowniki. Innym problemem jest mieszanie rzeczowników niepoliczalnych z liczbą mnogą. Po co utrudniać programistom spędzanie na nich więcej czasu? Sugeruję używanie liczby pojedynczej do wszystkiego. Jeśli istnieje / {id}, interfejs API powinien zwrócić pojedynczy rekord. Jeśli nie ma / {id}, który następuje, API powinien zwrócić kolekcję.
Daming Fu

3
@DamingFu Pojedynczy zasób może nie zawsze być powiązany z identyfikatorem. na przykład. / user / {id} / nickName Patrząc na to, nie jest jasne, czy zwróci listę pseudonimów czy pojedynczego pseudonimu? Dlatego interfejsy API są bardziej intuicyjne, gdy używa liczby mnogiej. Tak, kilka słów będzie miało nieregularną liczbę mnogą. Dla kogoś, kto czyta liczbę mnogą, nie stanowi problemu. Problem występuje tylko podczas pisania podpisu API. Ale częstotliwość takich słów nie jest wysoka, również znalezienie liczby mnogiej dowolnego słowa nie jest czasochłonne. To kompromis, który powinniśmy zaakceptować, aby interfejsy API były bardziej intuicyjne.
kishor borate

15

Z punktu widzenia konsumenta interfejsu API punkty końcowe powinny być przewidywalne

Idealnie...

  1. GET /resources powinien zwrócić listę zasobów.
  2. GET /resource powinien zwrócić kod statusu na poziomie 400.
  3. GET /resources/id/{resourceId} powinien zwrócić kolekcję z jednym zasobem.
  4. GET /resource/id/{resourceId} powinien zwrócić obiekt zasobu.
  5. POST /resources powinien wsadowo tworzyć zasoby.
  6. POST /resource powinien utworzyć zasób.
  7. PUT /resource powinien zaktualizować obiekt zasobu.
  8. PATCH /resource powinien zaktualizować zasób, publikując tylko zmienione atrybuty.
  9. PATCH /resources powinien wsadowo aktualizować zasoby, publikując tylko zmienione atrybuty.
  10. DELETE /resourcespowinien usunąć wszystkie zasoby; tylko żartuję: kod statusu 400
  11. DELETE /resource/id/{resourceId}

Takie podejście jest najbardziej elastyczne i bogate w funkcje, ale także najbardziej czasochłonne. Jeśli więc się spieszysz (co jest zawsze w przypadku tworzenia oprogramowania), po prostu podaj swój punkt końcowy resourcelub liczbę mnogą resources. Wolę formę pojedynczą, ponieważ daje ona możliwość introspekcji i oceny programowej, ponieważ nie wszystkie formy liczby mnogiej kończą się na „s”.

To powiedziawszy, bez względu na powód, z którego najczęściej wybierał programista, jest stosowanie liczby mnogiej. To jest ostatecznie droga, którą wybrałem i jeśli spojrzysz na popularne api githubi twittertakie właśnie są.

Niektóre kryteria podejmowania decyzji mogą być:

  1. Jakie są moje ograniczenia czasowe?
  2. Jakie operacje pozwolę moim klientom robić?
  3. Jak wygląda ładunek żądania i wyniku?
  4. Czy chcę móc używać refleksji i analizować identyfikator URI w moim kodzie?

Więc to zależy od ciebie. Cokolwiek robisz, bądź konsekwentny.


1
Wydaje się, że wybrano liczbę mnogą, ponieważ programiści zdają się zakładać, że wszystkie zasoby są z natury częścią jakiejś kolekcji. Jednak „przyjęta konwencja” wydaje się wskazywać, że POST /userspowinien utworzyć jednego użytkownika, dodając go do kolekcji. Nie zgadzam się. POST /userspowinien utworzyć listę użytkowników (nawet jeśli jest to lista 1), gdzie POST /userpowinien utworzyć dokładnie jednego użytkownika. Nie widzę powodu, dla którego zarówno punkty końcowe w liczbie mnogiej, jak i w liczbie pojedynczej nie mogą współistnieć. Opisują różne zachowania i nie powinny nikogo dziwić ich funkcji.
zmiażdżyć

Czy nie ma konwencji określania identyfikatora zasobu na ścieżce? Jeśli tak, wydaje się, że jest to powszechnie zaniedbywane. Na przykład POST users/<id>utworzyłby nowego użytkownika.
Tom Russell

1
@TomRussell zwykle serwer tworzy identyfikator, więc nie znasz jeszcze identyfikatora POST.
Alex

3
@TomRussell, gdy klient określa (rodzaj) id podczas tworzenia nowego zasobu, częściej używa się go PUT /users/<id>zamiast POST. POSTma interpretację „dodaj to do kolekcji i określ identyfikator jako część tego”. PUTma interpretację „zaktualizuj (lub dodaj) ten zasób o tym identyfikatorze”. Więcej informacji na temat tej zasady można znaleźć na stronie restcookbook.com/HTTP%20Methods/put-vs-post .
Jochem Schulenklopper,

Nie sądzę, aby porównanie z interfejsem API usługi Twitters było uczciwe, ponieważ używają oni formy liczby mnogiej dla wszystkich punktów końcowych. Nie mieszają liczby mnogiej i liczby pojedynczej dla tych samych elementów.
Andrew T Finnell,

7

Moje dwa centy: metody, które spędzają czas zmieniając liczbę mnogą na liczbę pojedynczą lub odwrotnie, są marnowaniem cykli procesora. Mogę być oldschoolowy, ale w moich czasach rzeczy były nazywane tak samo. Jak wyszukać metody dotyczące ludzi? Żadna regularna ekspresja nie obejmie zarówno osoby, jak i osób bez niepożądanych skutków ubocznych.

Angielskie liczby mnogie mogą być bardzo dowolne i niepotrzebnie obciążają kod. Trzymaj się jednej konwencji nazewnictwa. Języki komputerowe miały dotyczyć przejrzystości matematycznej, a nie naśladowania języka naturalnego.


2
Adres ten dotyczy kodu, który próbuje „automatycznie generować / przekształcać” punkty końcowe (istnieje wiele bibliotek, które przyjmują wiele / osobliwość i próbują mapować); ma to jednak zastosowanie do jawnie wybranych nazw punktów końcowych, tak samo jak wybranie odpowiedniego słowa (niezależnie od tego, jak jest pluralizowane).
user2864740,

6

Wolę używać formy pojedynczej ze względu na prostotę i spójność.

Na przykład biorąc pod uwagę następujący adres URL:

/ klient / 1

Potraktuję klienta jako odbiór klienta, ale dla uproszczenia część kolekcji jest usuwana.

Inny przykład:

/ wyposażenie / 1

W takim przypadku wyposażenie nie jest poprawną liczbą mnogą. Traktowanie go jako zbioru sprzętu i usuwanie kolekcji dla uproszczenia sprawia, że ​​jest zgodny z przypadkiem klienta.


2
POST / klient wydaje się, że zastąpi jedynego klienta. To mój największy żal z używania pojedynczych nazw zasobów.
Andrew T Finnell,

2
@ andrew-t-finnell Czy POST /customerto nie jest samo - wstawić jednego klienta?
donmutti

Wstawia pojedynczego klienta do zbioru klientów. POST /customerczyta mi to tak, jakby to było wysyłanie do theklienta. Nie kolekcja klientów. Przyznaję jednak, że preferowana jest liczba mnoga. Tak długo, jak nie są mieszane tak jak inne odpowiedzi. To byłoby niezwykle mylące.
Andrew T Finnell

W tym przypadku „wysyłanie do klienta” nie ma sensu. POST nie zastępuje, wstawia. Może gdyby to był POST / klient / 1, widziałbym dylemat, ale nawet to nie ma większego sensu z perspektywy REST, ponieważ co wstawiasz? Byłby to / klient / 1 / faktura lub / klient / 1 / rachunek itp.
cholera

5

Identyfikator trasy powinien być wyświetlany tak samo jak indeks listy, a nazewnictwo powinno być kontynuowane odpowiednio.

numbers = [1, 2, 3]

numbers            GET /numbers
numbers[1]         GET /numbers/1
numbers.push(4)    POST /numbers
numbers[1] = 23    UPDATE /numbers/1

Ale niektóre zasoby nie używają identyfikatorów na swoich trasach, ponieważ jest tylko jeden lub użytkownik nigdy nie ma dostępu do więcej niż jednego, więc nie są to listy:

GET /dashboard
DELETE /session
POST /login
GET /users/{:id}/profile
UPDATE /users/{:id}/profile

4

W przypadku konwencji nazewnictwa zwykle można bezpiecznie powiedzieć „wybierz jedną i trzymaj się jej”, co ma sens.

Jednak po konieczności wyjaśnienia REST wielu osobom reprezentowanie punktów końcowych jako ścieżek w systemie plików jest najbardziej ekspresyjnym sposobem na zrobienie tego.
Jest bezstanowy (pliki istnieją lub nie istnieją), hierarchiczny, prosty i znajomy - już wiesz, jak uzyskać dostęp do plików statycznych, lokalnie lub przez http.

W tym kontekście reguły językowe mogą doprowadzić cię tylko do tego, co następuje:

Katalog może zawierać wiele plików i / lub podkatalogów, dlatego jego nazwa powinna być w liczbie mnogiej.

I lubię to.
Chociaż z drugiej strony - to twój katalog, możesz nazwać go „zasobem lub wieloma zasobami”, jeśli tego chcesz. To nie jest naprawdę ważna rzecz.

Co ważne, jeśli umieścisz plik o nazwie „123” w katalogu o nazwie „resourceS” (w wyniku czego /resourceS/123), nie możesz oczekiwać, że będzie on dostępny za pośrednictwem/resource/123 .

Nie staraj się, aby był mądrzejszy niż musi być - zmiana z liczby mnogiej na singluar w zależności od liczby dostępnych zasobów może być dla niektórych estetyczna, ale nie jest skuteczna i nie ma sensu system hierarchiczny .

Uwaga: Technicznie możesz tworzyć „dowiązania symboliczne”, aby /resources/123można było uzyskać do nich dostęp za pośrednictwem /resource/123, ale to pierwsze wciąż musi istnieć!


3

Rzucić okiem na Google „s API Design Guide: Nazwy zasobów dla innego spojrzenia na nazewnictwa zasobów.

W skrócie:

  • Kolekcje są nazywane liczbą mnogą.
  • Poszczególne zasoby są nazywane ciągiem.
|--------------------------+---------------+-------------------+---------------+--------------|
| API Service Name         | Collection ID | Resource ID       | Collection ID | Resource ID  |
|--------------------------+---------------+-------------------+---------------+--------------|
| //mail.googleapis.com    | /users        | /name@example.com | /settings     | /customFrom  |
| //storage.googleapis.com | /buckets      | /bucket-id        | /objects      | /object-id   |
|--------------------------+---------------+-------------------+---------------+--------------|

Warto przeczytać, jeśli myślisz o tym temacie.


2

Używanie liczby mnogiej dla wszystkich metod jest bardziej praktyczne przynajmniej w jednym aspekcie: jeśli opracowujesz i testujesz interfejs API zasobów za pomocą Postmana (lub podobnego narzędzia), nie musisz edytować identyfikatora URI podczas przełączania z GET na PUT na POST itp. .


1
To nie jest dla mnie argument, ponieważ Listonosz oferuje kolekcje, więc możesz zapisać wszystkie zasoby jako różne elementy kolekcji i przetestować je indywidualnie. Wystarczy wybrać zasób z kolekcji, nie trzeba za każdym razem edytować parametrów / metod / etc.
Wirone,

1

Oba przedstawienia są przydatne. Od jakiegoś czasu używałem liczby pojedynczej, przegięcie może być trudne. Moje doświadczenie w tworzeniu ściśle pojedynczych interfejsów API REST, programiści używający punktu końcowego nie mają pewności, jaki może być kształt wyniku. Wolę teraz używać terminu, który najlepiej opisuje kształt odpowiedzi.

Jeśli wszystkie zasoby są na najwyższym poziomie, możesz uciec od pojedynczych reprezentacji. Unikanie przegięcia to duża wygrana.

Jeśli wykonujesz jakiekolwiek głębokie linkowanie w celu reprezentowania zapytań dotyczących relacji, programistom piszącym przeciwko Twojemu interfejsowi API można pomóc, stosując bardziej rygorystyczną konwencję.

Moja konwencja polega na tym, że każdy poziom głębokości w URI opisuje interakcję z zasobem nadrzędnym, a pełny URI powinien pośrednio opisywać to, co jest pobierane.

Załóżmy, że mamy następujący model.

interface User {
    <string>id;
    <Friend[]>friends;
    <Manager>user;
}

interface Friend {
    <string>id;
    <User>user;
    ...<<friendship specific props>>
}

Gdybym musiał udostępnić zasób, który pozwala klientowi uzyskać menedżera określonego znajomego określonego użytkownika, mogłoby to wyglądać mniej więcej tak:

GET /users/{id}/friends/{friendId}/manager

Oto kilka innych przykładów:

  • GET /users - wymień zasoby użytkowników w globalnej kolekcji użytkowników
  • POST /users - utwórz nowego użytkownika w globalnej kolekcji użytkowników
  • GET /users/{id} - pobierz określonego użytkownika z globalnej kolekcji użytkowników
  • GET /users/{id}/manager - uzyskaj menedżera określonego użytkownika
  • GET /users/{id}/friends - zdobądź listę przyjaciół użytkownika
  • GET /users/{id}/friends/{friendId} - zdobądź określonego przyjaciela użytkownika
  • LINK /users/{id}/friends - dodaj powiązanie znajomego do tego użytkownika
  • UNLINK /users/{id}/friends - usuń powiązanie znajomego z tym użytkownikiem

Zwróć uwagę, w jaki sposób każdy poziom jest mapowany na rodzica, na którym można działać. Używanie różnych rodziców dla tego samego obiektu jest sprzeczne z intuicją. Pobieranie zasobu w GET /resource/123nie pozostawia żadnych wskazówek, że należy utworzyć nowy zasób wPOST /resources


1

Wiem, że większość ludzi decyduje, czy użyć liczby mnogiej czy pojedynczej. Problem, który nie został tutaj rozwiązany, polega na tym, że klient musi wiedzieć, którego używasz, i zawsze może popełnić błąd. Stąd moja sugestia.

Co powiesz na oba? I przez to mam na myśli użycie liczby pojedynczej dla całego API, a następnie utworzenie tras do przekazywania żądań złożonych w liczbie mnogiej do formy pojedynczej. Na przykład:

GET  /resources     =     GET  /resource
GET  /resources/1   =     GET  /resource/1
POST /resources/1   =     POST /resource/1
...

Dostajesz obraz. Nikt się nie myli, minimalny wysiłek, a klient zawsze dobrze to zrobi.


1
Jeśli wykonujesz przekierowania 302, a pamięć podręczna przechowuje wszystko dwa razy, źle skonfigurowałeś pamięć podręczną. Pamięć podręczna nie powinna przechowywać przekierowań 302.
wynnset

2
Jeśli Twój klient zawsze używa /resourcesi zawsze /resourcezostajesz przekierowany , zrobiłeś to źle. Jeśli ktoś inny korzysta z Twojego API, może albo użyć poprawnego adresu URL bezpośrednio, albo zostać przekierowanym (co działa, ale jest złe) i to Ty otworzyłeś niewłaściwy sposób.
maaartinus

Nie jestem pewien, co masz na myśli „źle” - to bardzo subiektywne. To naprawdę nie jest źle, ponieważ działa.
wynnset

Zwiększa to koszty utrzymania, koszty zrozumienia i wymaganą ilość kodu.
Will Sheppard,

1

Nie lubię, gdy {id}część adresów URL pokrywa się z podrzędnymi zasobamiid teoretycznie może to być cokolwiek i byłoby niejednoznaczne. Łączy różne pojęcia (identyfikatory i nazwy pod-zasobów).

Podobne problemy są często postrzegane w enumstałych lub folder struktur, gdzie różne koncepcje są mieszane (na przykład, gdy masz foldery Tigers, Lionsa Cheetahs, a następnie również folder o nazwieAnimals na tym samym poziomie - to nie ma sensu, jak ktoś jest podzbiorem inny).

Ogólnie rzecz biorąc, myślę, że ostatnia nazwana część punktu końcowego powinna być pojedyncza, jeśli dotyczy ona pojedynczego elementu na raz, i liczba mnoga, jeśli dotyczy listy elementów.

Punkty końcowe, które dotyczą jednego użytkownika:

GET  /user             -> Not allowed, 400
GET  /user/{id}        -> Returns user with given id
POST /user             -> Creates a new user
PUT  /user/{id}        -> Updates user with given id
DELETE /user/{id}      -> Deletes user with given id

Następnie istnieje osobny zasób do wykonywania zapytań na użytkownikach, które zazwyczaj zwracają listę:

GET /users             -> Lists all users, optionally filtered by way of parameters
GET /users/new?since=x -> Gets all users that are new since a specific time
GET /users/top?max=x   -> Gets top X active users

A oto kilka przykładów pod-zasobu, który zajmuje się konkretnym użytkownikiem:

GET /user/{id}/friends -> Returns a list of friends of given user

Zaprzyjaźnij się (link wiele do wielu):

PUT /user/{id}/friend/{id}     -> Befriends two users
DELETE /user/{id}/friend/{id}  -> Unfriends two users
GET /user/{id}/friend/{id}     -> Gets status of friendship between two users

Nigdy nie ma dwuznaczności, a mnoga lub pojedyncza nazwa zasobu jest wskazówką dla użytkownika, czego może się spodziewać (lista lub obiekt). Nie ma żadnych ograniczeń dotyczących ids, teoretycznie umożliwiając posiadanie użytkownika o identyfikatorze newbez nakładania się na (potencjalnie przyszłą) nazwę pod-zasobu.


1

Użyj liczby pojedynczej i skorzystaj z angielskiej konwencji, np. W „Business Directory”.

Wiele rzeczy czyta się w ten sposób: „Book Book”, „Dog Pack”, „Art Gallery”, „Festival Film”, „Car Lot” itp.

To wygodnie dopasowuje ścieżkę adresu URL od lewej do prawej. Typ elementu po lewej stronie. Ustaw typ po prawej stronie.

Czy GET /usersnaprawdę kiedykolwiek pobiera zestaw użytkowników? Zazwyczaj nie. Pobiera zestaw kodów pośredniczących zawierających klucz i być może nazwę użytkownika. Tak czy /usersinaczej tak naprawdę nie jest . Jest to indeks użytkowników lub „indeks użytkowników”, jeśli wolisz. Dlaczego nie nazwać tego tak? Jest to /user/index. Ponieważ nazwaliśmy typ zestawu, możemy mieć wiele typów pokazujących różne prognozy użytkownika bez uciekania się do parametrów zapytania np . user/phone-listLub /user/mailing-list.

A co z użytkownikiem 300? Nadal jest /user/300.

GET /user/index
GET /user/{id}

POST /user
PUT /user/{id}

DELETE /user/{id}

Na zakończenie HTTP może mieć tylko jedną odpowiedź na pojedyncze żądanie. Ścieżka zawsze odnosi się do pojedynczego czegoś.


-1

Dla mnie liczba mnoga manipuluje kolekcją , podczas gdy liczba pojedyncza manipuluje przedmiotem w tej kolekcji.

Kolekcja pozwala na metody GET / POST / DELETE

Pozycja pozwala na metody GET / PUT / DELETE

Na przykład

POST na / uczniowie dodadzą nowego ucznia do szkoły.

USUŃ włączone / uczniowie usuną wszystkich uczniów w szkole.

USUŃ na / student / 123 usunie ucznia 123 ze szkoły.

Może to wydawać się nieistotne, ale niektórzy inżynierowie czasami zapominają o identyfikatorze. Jeśli trasa była zawsze w liczbie mnogiej i wykonano operację USUŃ, możesz przypadkowo wyczyścić swoje dane. Podczas gdy brak identyfikatora w liczbie pojedynczej zwróci trasę 404, nie znaleziono.

Aby dalej rozwinąć przykład, jeśli API miałoby ujawniać wiele szkół, to coś w tym rodzaju

USUŃ na / school / abc / students usunie wszystkich uczniów w szkole abc.

Wybór właściwego słowa czasami stanowi wyzwanie sam w sobie, ale lubię zachować wiele kolekcji. Np. cart_itemsLub cart/itemsczuje się dobrze. W przeciwieństwie do usuwania cart, usuwa sam obiekt koszyka, a nie elementy w koszyku;).


Czy to nie powinno być podzielone na / cart i / cart / item (s)? Następnie możesz zaadresować cały koszyk (np. Z usunięciem) lub pojedyncze elementy?
Rob Grant

@RobertGrant Czy to nie byłoby „/ carts / items / 123”? (np. dlaczego „koszyk”, a nie „wózki” jest regułą „zawsze w liczbie mnogiej”?)
user2864740,

1
Argumentowałbym, że jeśli kod produkcyjny jest zarejestrowany i jest w stanie wykonać usunięcie wszystkich elementów koszyka, istnieją większe problemy niż konwencja nazewnictwa. Prawdopodobny kaptur, który zapamiętają i nad identyfikatorem, jest znacznie mniejszy.
Andrew T Finnell,

czy ktoś kiedykolwiek stworzyłby punkt końcowy, który po prostu usuwa całą kolekcję? Wydaje mi się to bardzo niebezpieczne i prawdopodobnie również dlatego, że REST tak naprawdę nie obsługuje usuwania wsadowego. (musisz zawinąć tablicę w obiekt). Gdybym absolutnie potrzebował punktu końcowego do usunięcia całej kolekcji,
upewniłbym

-1

Co powiesz na:

/resource/(nie /resource)

/resource/ oznacza, że ​​folder zawiera coś o nazwie „zasób”, jest to folder „resouce”.

Myślę też, że konwencja nazewnictwa tabel bazy danych jest taka sama, na przykład tabela o nazwie „użytkownik” jest „tabelą użytkownika”, zawiera coś zwanego „użytkownikiem”.


-2

Wolę używać zarówno liczby mnogiej ( /resources), jak i pojedynczej ( /resource/{id}), ponieważ uważam, że bardziej wyraźnie oddziela logikę między pracą nad kolekcją zasobów a pracą nad pojedynczym zasobem.

Jako ważny efekt uboczny tego, może również pomóc w zapobieganiu niewłaściwemu użyciu API. Rozważmy na przykład przypadek, w którym użytkownik błędnie próbuje uzyskać zasób, określając identyfikator jako parametr w następujący sposób:

GET /resources?Id=123

W tym przypadku, gdy używamy liczby mnogiej, serwer najprawdopodobniej zignoruje parametr Id i zwróci listę wszystkich zasobów. Jeśli użytkownik nie będzie ostrożny, pomyśli, że połączenie się powiodło, i użyje pierwszego zasobu na liście.

Z drugiej strony, gdy używasz formy pojedynczej:

GET /resource?Id=123

serwer najprawdopodobniej zwróci błąd, ponieważ identyfikator nie jest określony we właściwy sposób, a użytkownik będzie musiał zrozumieć, że coś jest nie tak.


1
Dlaczego miksujesz tutaj idiomy? Używasz właściwej notacji URI w pierwszym akapicie, a następnie przełączasz się na parametry zapytania? Korzystanie z parametrów zapytania w celu uzyskania zasobu o identyfikatorze 123 jest tutaj całkowicie nie na miejscu.
Andrew T Finnell,

To był oczywiście błąd. Zaktualizowałem teraz swoją odpowiedź. Dzięki, że to zauważyłeś.
pberggreen

Po ponownym przegłosowaniu spojrzałem na to, co napisałem i zdałem sobie sprawę, że oryginalny post był poprawny. Chodzi mi dokładnie o to, że jeśli użytkownik zrobi coś złego, to użycie liczby mnogiej + liczby pojedynczej da lepszy komunikat o błędzie niż użycie liczby mnogiej.
pberggreen

Nadal uważam, że jest to mylące dla tego problemu. Liczba mnoga polega na tym, że jest to kolekcja. A liczba na końcu jest indeksem w kolekcji. Co się stanie, jeśli sam zdobędziesz / zasób? Używanie razem liczby mnogiej i pojedynczej jest dość mylące. Saying / resources / 123 mówi: Pobierz mój zasób 123 do zasobnika zasobów.
Andrew T Finnell,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.