PROJEKTOWANIE REST API - pobieranie zasobu przez REST z różnymi parametrami, ale tym samym wzorcem adresu URL

92

Mam pytanie związane z projektowaniem adresu URL REST. Znalazłem kilka odpowiednich postów tutaj: Różne reprezentacje RESTful tego samego zasobu i tutaj: URL RESTful do zasobu GET według różnych pól, ale odpowiedzi nie są do końca jasne, jakie są najlepsze praktyki i dlaczego. Oto przykład.

Mam adresy URL REST do reprezentowania zasobów „użytkowników”. Mogę OTRZYMAĆ użytkownika z identyfikatorem lub adresem e-mail, ale reprezentacja adresu URL pozostaje taka sama dla obu. Przeglądając wiele blogów i książek, widzę, że ludzie robili to na wiele różnych sposobów. Na przykład

przeczytaj tę praktykę w książce i gdzieś na stackoverflow (nie mogę znaleźć linku ponownie)

GET /users/id={id}
GET /users/email={email}

przeczytaj tę praktykę na wielu blogach

GET /users/{id}
GET /users/email/{email}

Parametry zapytania są zwykle używane do filtrowania wyników zasobów reprezentowanych przez adres URL, ale widziałem również tę praktykę

GET /users?id={id}
GET /users?email={email}

Moje pytanie brzmi: która spośród wszystkich tych praktyk byłaby najbardziej sensowna dla programistów korzystających z interfejsu API i dlaczego? Uważam, że nie ma żadnych zasad ustalonych w kamieniu, jeśli chodzi o projekty adresów URL REST i konwencje nazewnictwa, ale chciałem tylko wiedzieć, jaką drogę powinienem obrać, aby pomóc programistom lepiej zrozumieć API.

Cała pomoc doceniona!

shahshi15
źródło
1
Wiem, że to trochę stare, ale szukając podobnych zasobów, natknąłem się na to pytanie i to, którego szukałeś. Wierzę, że to ten jeden stackoverflow.com/a/9743414/468327
Joel Berger

Odpowiedzi:

104

Z mojego doświadczenia GET /users/{id} GET /users/email/{email}wynika, że ​​jest to najczęstsze podejście. Spodziewałbym się również, że metody zwrócą błąd 404 Not Found, jeśli użytkownik nie istnieje z podanym idlub email. Nie zdziwiłbym się GET /users/id/{id}również (choć moim zdaniem jest to zbędne).

Komentarze na temat innych podejść

  1. GET /users/id={id} GET /users/email={email}
    • Myślę, że nie widziałem tego, a gdybym to zobaczył, byłoby to bardzo zagmatwane. To prawie tak, jakby próbowało imitować parametry zapytania za pomocą parametrów ścieżki.
  2. GET /users?id={id} GET /users?email={email}
    • Myślę, że trafiłeś w sedno, kiedy wspomniałeś o używaniu parametrów zapytania do filtrowania.
    • Czy kiedykolwiek miałoby sens nazywanie tego zasobu zarówno literą, jakid i email(np. GET /users?id={id}&email={email})? Jeśli nie, nie użyłbym takiej metody z jednym zasobem.
    • Spodziewałbym się tej metody do pobierania listy użytkowników z opcjonalnymi parametrami zapytania do filtrowania, ale nie spodziewałbym się id, emailże wśród parametrów będzie znajdować się jakikolwiek unikalny identyfikator. Na przykład: GET /users?status=BANNEDmoże zwrócić listę zablokowanych użytkowników.

Sprawdź tę odpowiedź z pokrewnego pytania.

DannyMo
źródło
Dziękuję za wkład. Właściwie masz rację. Wygląda na to, że przeczytałem numer 1 w książce „RESTful java with Jax-rs”, ale trochę wyrwany z kontekstu. Ale bardzo dobrze pamiętam, że przeczytałem to jako odpowiedź na jedno z pytań dotyczących samego stackoverflow (wierz mi, kiedy mówię, że bardzo się staram znaleźć ten link, aby udowodnić również moim kolegom :)) szukam. Kilka opinii na temat projektu. Dziękuję Ci!
shahshi15,
PS: Nie jestem pewien, czy zamieszczanie tego tutaj jest sprzeczne z regułą, ale prawdopodobnie możesz rzucić trochę światła na jedno z moich innych pytań? Proszę? :) stackoverflow.com/questions/20508008/…
shahshi15
tylko po to, aby wyjaśnić Twoje oświadczenie o redundancji dla /users/id/{id}, pozwala to na rozszerzoną funkcjonalność, po prostu pozwala na dostęp do jednego zasobu za pomocą kilku identyfikatorów (id, guid, name). również odpowiedział tutaj
Daniel Dubovski
4
Moja odpowiedź byłaby taka, że ​​właściwym sposobem odniesienia się na przykład do konkretnego Użytkownika byłoby / users / {id}. Jeśli chcesz znaleźć użytkowników na podstawie określonego adresu e-mail, który nie jest unikalnym identyfikatorem użytkowników, to zrobiłbym / users? Email = {email}, ponieważ jest to sposób na filtrowanie zbioru użytkowników, a nie identyfikowanie jednego konkretnego zasobu na podstawie jego zasobu identyfikator jako / users / {id} to.
Kevin M
Świetna odpowiedź! Kciuki za to. Jedyne, co zalecałbym, to również trochę zmienić API, ponieważ REST powinien koncentrować się na nazwach, więc mapowanie zasobów powinno wyglądać mniej więcej tak: GET /user/1234a nieGET /users/123
Sammy.
38

Patrząc na to pragmatycznie, masz zbiór użytkowników:

/users   # this returns many

Każdy użytkownik ma dedykowaną lokalizację zasobów:

/users/{id}    # this returns one

Masz również kilka sposobów wyszukiwania użytkowników:

/users?email={email}
/users?name=*bob*

Ponieważ są to wszystkie parametry zapytania do / users, wszystkie powinny zwracać listy ... nawet jeśli jest to lista 1.

Napisałem tutaj wpis na blogu o pragmatycznym projekcie RESTful API, który mówi o tym między innymi tutaj: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

Vinay Sahni
źródło
Dzięki za odpowiedź vinay. Ale jeśli pomyślimy o tym z innej perspektywy, {email} tak jak {id} ZAWSZE zwróci jeden wynik z powrotem. Jeśli naprawdę szukamy, dlaczego nie użyć? Id = {id} tak samo jak w przypadku e-maila? Zastanawiałem się, czy w ogóle jest konsekwencja. PS: nowy w stackoverflow, nie wiedziałem, że mogę podać tylko jedną odpowiedź. Ale dzięki za twoją odpowiedź! Doceniam to. Może ty też możesz mi pomóc z tym: stackoverflow.com/questions/20508008/…
shahshi15
7
W tym miejscu pojawia się aspekt „projektowania” projektowania API. Twój zasób powinien mieć dedykowaną lokalizację. Gdzie jest lokalizacja? Czy lokalizacja jest według identyfikatora? Jeśli tak, to nie „wyszukujesz” według identyfikatora, ale „otrzymujesz” według identyfikatora. Ale szukasz wszystkiego innego. Oczywiście nie ma tutaj sztywnej reguły. To tylko perspektywa :)
Vinay Sahni,
6
Twierdzę również, że w ten sposób twoje API jest bardziej stabilne. Naprawdę wiesz, że Twój identyfikator jest unikalny, ale czy naprawdę jesteś pewien co do adresu e-mail? Nawet jeśli jest unikalny, prawdopodobnie nie jest trwały, ponieważ użytkownik może go zmienić. Tak więc / users? Email = {email} wyjaśnia, że ​​jest to rzeczywiście zapytanie, a nie dostęp do zasobu o stałym identyfikatorze.
lex82
Uważam, że jest to właściwie bardziej poprawna odpowiedź. Filtrowanie kolekcji według adresu e-mail może być oparte na symbolach wieloznacznych, więc nie zawsze zwróci tylko jeden wynik.
Kevin M
@VinaySahni Co powiesz o wzorcu takim jak: /user?by=email&email="[email protected] "/ user? By = id & id =" xashkhx "/ users? FilterA =" a "& filterB =" b "(liczba mnoga dla wielu użytkowników)
Shasak
2

O zasobach użytkowników

na ścieżce /userszawsze otrzymasz zbiór zwróconych zasobów użytkownika.

na ścieżce /users/[user_id]możesz spodziewać się kilku rzeczy:

  1. powinieneś otrzymać pojedynczy zasób reprezentujący zasób użytkownika z jego identyfikatorem [user_id] lub
  2. a nie znaleziono odpowiedzi 404, jeśli nie istnieje użytkownik o identyfikatorze [identyfikator_użytkownika] lub
  3. zabroniony 401, jeśli nie masz dostępu do żądanego zasobu użytkownika.

Każdy singleton jest jednoznacznie identyfikowany przez swoją ścieżkę i identyfikator, których używasz do znajdowania zasobu. Nie jest możliwe użycie kilku ścieżek dla singletona.

Możesz zapytać o ścieżkę za /userspomocą parametrów zapytania ( GETParametry). Spowoduje to zwrócenie kolekcji z użytkownikami spełniającymi żądane kryteria. Zwracana kolekcja powinna zawierać zasoby użytkownika, wszystkie wraz z ich identyfikującą ścieżką zasobów w odpowiedzi.

Parametrami może być dowolne pole obecne w zasobach kolekcji; firstName, lastName,id

O e-mailu

Wiadomość e-mail może być zasobem lub właściwością / polem zasobu użytkownika.

- E-mail jako własność użytkownika:

Jeśli pole jest własnością użytkownika, odpowiedź użytkownika wyglądałaby mniej więcej tak:

{ 
  id: 1,
  firstName: 'John'
  lastName: 'Doe'
  email: '[email protected]'
  ...
}

Oznacza to, że nie ma specjalnego punktu końcowego na e-maile, ale można teraz znaleźć użytkownika przez jego elektroniczną wysyłając następującą prośbę: /[email protected]. Który (zakładając, że e-maile są unikalne dla użytkowników) zwróciłby kolekcję z jednym elementem użytkownika, który pasuje do e-maila.

- E-mail jako zasób:

Ale jeśli e-maile od użytkowników są również zasobami. Następnie możesz stworzyć API, które /users/[user_id]/emailszwraca kolekcję adresów e-mail dla użytkownika o identyfikatorze user_id. /users/[user_id]/emails/[email_id]zwraca e-mail użytkownika z user_id i ['email_id']. To, czego użyjesz jako identyfikatora, zależy od Ciebie, ale trzymałbym się liczby całkowitej. Możesz usunąć wiadomość e-mail od użytkownika, wysyłając DELETEżądanie na ścieżkę identyfikującą wiadomość e-mail, którą chcesz usunąć. Na przykład, DELETEon /users/[user_id]/emails/[email_id]usunie e-mail z email_id, którego właścicielem jest użytkownik z user_id. Najprawdopodobniej tylko ten użytkownik może wykonać tę operację usuwania. Inni użytkownicy otrzymają odpowiedź 401.

Jeśli użytkownik może mieć tylko jeden adres e-mail, możesz go trzymać. /users/[user_id]/email Spowoduje to zwrócenie pojedynczego zasobu. Użytkownik może zaktualizować swój adres PUTe-mail, wprowadzając adres e-mail pod tym adresem URL.

Więdnąć
źródło