Problem z zaprojektowaniem ścieżek w API

0

Mam aplikację, w której użytkownik może uznać recenzję produktu za pomocną lub niepomocną. Mam tabelę ReviewLike (wiem, słaba nazwa, ma ktoś lepszą?) z kolumnami ReviewId, CustomerId, IsLike. Nie wiem, jak zaprojektować ścieżki w REST API do tego. Wymyśliłem na razie coś takiego:
POST api/review-likes?reviewId=100 {isLike=true}
PUT api/review-likes?reviewId=100 {isLike=false}
DELETE api/review-likes?reviewId=100

  1. Czy powinienem dołączyć do tabeli ReviewLike kolumnę z unikalnym id? Z jednej strony jest to niepotrzebne, ale z drugiej strony ścieżki w API ładniej by wyglądały.
  2. Czy można to jakoś ładniej rozwiązać? Ten drugi endpoint PUT wydaje mi się dziwny i nienaturalny.
2

Jeśli chcesz po czymś identyfikować to użyj to jako pathParameter, jeśli chcesz po czymś filtrować to jako queryParameter. Każda recenzja imo powinna mieć swoje ID i potem jeśli chcesz dostać się do zasobu to:
api/review/{reviewId}
Jeśli chcesz dostać np wszystkie recenzje użytwokownika to
api/user/{userId}/reviews
Jeśli chcesz np wszystkie recenzje usera które są polubieniami to
api/user/{userId}/reviews?isLike=true

6

Biorąc pod uwagę, że w tym wypadku aggregate rootem jest review, poszedłbym w coś takiego:

POST /api/reviews/:id/like   # <- dodaje lajka
DELETE /api/reviews/:id/like # <- usuwa lajka
0

@Patryk27: Tylko czy wtedy nie będę miał RPC? Żeby nie było, takie rozwiązanie mi się podoba, ale ktoś kiedyś pisał, że RPC to zło ;(

1

Dlaczego RPC to zło?

Tak czy siak: z jakiegoś powodu aktualnie często propagowane są API, które wyglądają jak lekki wrapper na bazę danych bez żadnych dodatkowych funkcjonalności - podane przeze mnie rozwiązanie jest IMO wysokopoziomowe, czytelne i łatwe w zaimplementowaniu, stąd poszedłbym w tę stronę, nawet jeśli wygląda na rpc-like.

0

Może niekoniecznie zło, ale @Leroy: tu Plus jest taki, ze podejscie restowe jest ustandaryzowane i zrozumiale. Co innego 30 roznych serwisow z podejsciem "RPC over HTTP", bez porzadnej dokumentacji jest pograne, IMO :)

2

Podejście RESTowe miało być ustandaryzowane - ostatecznie wyszła z tego jedna wielka ciapa, którą każdy implementuje w inny sposób (o kodach statusów nie wspominając).

RESTowe API bez dokumentacji jest dokładnie tak samo trudne w przetwarzaniu, jak te wszystkie JSON-RPC itd. i w każdym przypadku bez dokumentacji błądzisz po omacku.

0

Tak z ciekawości: jak te ścieżki powinny wyglądać w "prawdziwym" REST API?

0

Zdefiniuj prawdziwe REST API.

0

A co jeśli w innej zdefiniowali inaczej?

0

No ok, ale chyba musi być jakaś "biblia" REST API, tak jak to jest w DDD.

2

https://tools.ietf.org/html/rfc7231 + https://roy.gbiv.com/pubs/dissertation/top.htm przynajmniej "historycznie" więc to taki Stary Testament

2

REST w żaden sposób nie definiuje tego jak ścieżki/url powinny wyglądać, rozdział 6.2.4 Binding Semantics to URI z przytoczonej wyżej rozprawy doktorskiej

Semantics are a by-product of the act of assigning resource identifiers and populating those resources with representations. At no time whatsoever do the server or client software need to know or understand the meaning of a URL -- they merely act as a conduit through which the creator of a resource (a human naming authority) can associate representations with the semantics identified by the URI. In other words, there are no resources on the server; just mechanisms that supply answers across an abstract interface defined by resources. It may seem odd, but this is the essence of what makes the Web work across so many different implementations.

także wszystkie przytoczone w tym temacie ścieżki są poprawne z punktu widzenia REST.

3
nobody01 napisał(a):

@Patryk27: Tylko czy wtedy nie będę miał RPC?

Ale czemu właściwie uważasz, że to RPC?
Jest URL wskazujący na zasób, operacje są zróżnicowane metodami HTTP, a nie urlami albo zawartością requestu. Jak dla mnie to jest wystarczająco restowe.

RPC by było, gdybyś na jeden endpoint słał <AddLikeRequest> albo <DeleteLikeRequest>.

0

Ok, w sumie jak teraz czytam, to okazuje się, że w "restowych" urlach jednak mogą pojawiać się czasowniki dla jakichś niestandardowych operacji. https://stackoverflow.com/a/27122233 Chociaż nie zdaniem wszystkich...

1

To chyba wśród tych, którzy robią tylko crudy, i to bez żadnego uwierzytelniania, bo restowo to nawet zalogować dobrze się nie da. :P

1

bo restowo to nawet zalogować dobrze się nie da

Da się.

POST /user/session
0

Przede wszystkim powinno być users, a potem w path powinien być id usera, bo przecież to sesja konkretnego użytkownika, a nie jakaś taka ogólna.

1

@somekind:

  1. Niekoniecznie musi być user, zależy od konwencji, to samo z ID, bo nie musisz tworzyć sesji dla usera o ID x a zwyczajnie sesję, na podstawie podanych danych.
  2. To możesz użyć:
    • POST /sessions
    • POST /users/me/sessions
    • POST /me/sessions

Cokolwiek będzie Tobie pasować do reszty aplikacji.

1

Myślę, że spokojnie możesz to na jakimś stacku napisać, i zobaczyć świętą wojnę o to, że musi być users, a sessions to nie jest zasób :P

0

Onaniści "jedynego słusznego podejścia" znajdą się wszędzie, ale to nie oznacza, że ich "jedyne słuszne podejście" jest jedyne albo słuszne.

0

Ale każda z frakcji twierdzi, że tylko ich rest jest restowy. Dlatego napisałem, że nie da się dobrze restowo zalogować, bo zawsze ktoś "udowodni", że to nie jest rest. :P

1

Przede wszystkim nie pakowałbym domeny na siłę do ścieżki API. Skomplikowana domena i REST API to powinny być dwie dość osobne warstwy. Co innego CRUD... albo "Encja na twarz i pchasz".
Możesz zrobić np:

POST /reviews/{id}/upvote
POST /reviews/{id}/downvote

gdzie ciałem będzie wiadomość... choćby pusta. Bardzo.. emm... zasobowa reprezentacja domeny.

0

@AreQrm: co nazywasz "pustą wiadomością" w kontekście HTTP POST?

0

Odnośnie

AreQrm napisał(a):

Przede wszystkim nie pakowałbym domeny na siłę do ścieżki API. Skomplikowana domena i REST API to powinny być dwie dość osobne warstwy. Co innego CRUD... albo "Encja na twarz i pchasz".
Możesz zrobić np:

POST /reviews/{id}/upvote
POST /reviews/{id}/downvote

gdzie ciałem będzie wiadomość... choćby pusta. Bardzo.. emm... zasobowa reprezentacja domeny.

oraz

Patryk27 napisał(a):

Biorąc pod uwagę, że w tym wypadku aggregate rootem jest review, poszedłbym w coś takiego:

POST /api/reviews/:id/like   # <- dodaje lajka
DELETE /api/reviews/:id/like # <- usuwa lajka

mam pytanie. Dlaczego POST? (nie twierdzę, że powinno być PUT)

  • POST tworzy nowy zasób, a tutaj nie tworzymy nowego
  • POST nie jest "idempotent" - w tym przypadku ok
  • PUT aktualizuje zasób, ale powinien zostać wysłany w całości
  • PUT jest "idempotent" - w tym przypadku nie pasuje

Co, gdy mamy wykonywać czynności, które nie podpadają pod "tworzeniu zasobu" lub "aktualizację zasobu" (np. zatrzymać lub zrestartować aplikację)? W API Dockera zarządzanie kontenerami jest zrobione podobnie jak w zacytowanych postach, czyli: POST /containers/123/{start, stop, pause, kill, restart}.

To wynika z konwencji, jakiejś specyfikacji, czegoś innego?

Poza tym jest jeszcze PATCH, ale raczej mało popularny?

1

@Potat0x:
protokół http nie został w ogóle zaprojektowany do takich operacji, więc trudno mówić która jest właściwa. Nie mniej powstał nieformalny protokół, który mówi, że jak wykonujesz jakąś operacje to robisz to POST, jak coś pobierasz to GET.

1

POST tworzy nowy zasób, a tutaj nie tworzymy nowego

Ja powiedziałbym, że tutaj tworzymy nowy zasób o nazwie np. ReviewLike :-)

2
Potat0x napisał(a):

mam pytanie. Dlaczego POST? (nie twierdzę, że powinno być PUT)

  • POST tworzy nowy zasób, a tutaj nie tworzymy nowego
  • POST nie jest "idempotent" - w tym przypadku ok
  • PUT aktualizuje zasób, ale powinien zostać wysłany w całości
  • PUT jest "idempotent" - w tym przypadku nie pasuje

Generalnie POST się używa zawsze, gdy nic innego nie pasuje. :)
A co do PUT, to może też tworzyć zasoby, jeśli nie istnieją Nie wiem czy to nie pasuje tu bardziej. Ale nie wiem też, czy PUT w ogóle jest RESTful. :P

Poza tym jest jeszcze PATCH, ale raczej mało popularny?

No bo to piąte słowo, a CRUD ma cztery. ;)

PS. Czemu piszesz idempotentny po angielsku i w cudzysłowie?

0

@Potat0x: Bo dodajesz nowy Upvote/Like.
@somekind: chodziło mi raczej o brak ciała w ogóle. Źle to napisałem.

1 użytkowników online, w tym zalogowanych: 0, gości: 1