API: moje zwierzątka

1

Mam zwierzęta jako zasoby i do nich dwa endpointy:

  • pierwszy ma zwracać wszystkie zwierzęcia.
  • drugi ma zwracać wszystkie moje zwierzaki. Moje, czyli zalogowanego principala.

Mam dwa proste pytania do takiego springowego cruda, szukam inspiracji.

  1. Jakie nazwy API byście użyli do tych endpointów? /api/animals i /api/animals/me? A może /api/animals i /api/me/animals ? Albo jeszcze inaczej? Szukam jakiejś powszechnie akceptowalnej dobrej konwencji.

  2. Czy władowalibyście to do jednego AnimalController czy może podzielilibyście też na MyAnimalController? Albo - wiedząc, że jest więcej takich moich resourców, np. moje stajnie, moje weterynarze, moje .. - to czy może warto by po prostu stworzyć zbiorczy MeController? Raczej nie, bo brzmi jak dużo zależności...?

Można założyć, że mimo wszystko w serwisach jest tam jakaś logika.

1

Api/Animals i api/animals?userid=x i zostawiłbym w jednym kontrolerze

6

Jeśli strzelasz po id usera to:

  • /users/animals
  • /users/:id/animals

To czy w jednym kontrolerze czy w dwóch to jeden pies, ja bym pewnie zrobił w jednym, ale w dwóch też będzie okej. I tak puszczasz te same testy dla nich nie ważne w jakich kontrolerach są (bo masz do tego testy prawda)?

I ogólnie wystrzegaj się nazw takich jak "my" w aplikacjach. Je się łatwo wymyśla, ale na dłuższą metę potem czytasz kod i nie wiesz co to robi. Jeśli chcesz już tak nazywać kontroller to nazwij go LoggedUserAnimals, czy cokolwiek co może wyjaśnić istote zadania. Bo "my" może znaczyć różne rzeczy w różnych kontekstach, także wyświadcz sobie przysługę i wymyśl lepszą nazwę.

0

Oba wyżej przykłady zakładają przekazanie id, a co jeśli przekazuje zalogowanego usera poprzez cookie (access token) ? Kontroler odbiera objekt Authentication albo coś podobnego.

0

@Xorxorxor: Możesz próbować rozpoznawać użytkownika za pomocą UserDetails i wyświetlać mu tylko stworzone przez niego elementy:

@RestController
public class ItemsController {
    private final Map<String, Set<String>> items = new ConcurrentHashMap<>();

    @PostMapping("/items")
    public void addItem(@AuthenticationPrincipal UserDetails details, @RequestParam String item) {
        String username = details.getUsername();

        if (items.containsKey(username)) {
            items.get(username).add(item);
        } else {
            items.put(username, new HashSet<>(Set.of(item)));
        }
    }

    @GetMapping("/items")
    public Set<String> getItems(@AuthenticationPrincipal UserDetails details) {
        return items.getOrDefault(details.getUsername(), Set.of());
    }
}
1
Xorxorxor napisał(a):

Oba wyżej przykłady zakładają przekazanie id, a co jeśli przekazuje zalogowanego usera poprzez cookie (access token) ? Kontroler odbiera objekt Authentication albo coś podobnego.

Jeśli przekazujesz access token to nie po to żeby sterować logiką, tylko do autentykacji.

Rozumiem że chcesz mieć dwa, bezparametrowe endpointy, jeden zwracający wszystkie możliwe byty (Animals), a drugi zwracający byty przypisane do zalogowanego usera?

No mógłbyś się upodobnić do rest, iż wyobrazić sobie jakbyś coś takiego trzymał gdybyś chciał serwować zasoby.

Ja bym chyba poszedł w coś takiego

  • /animals
  • /user/animals

Wygląda jakby te dwa endpointy robiły różne rzeczy zupełnie, więc nie ma sensu ich do siebie upodabniać.

2

I fala, i cząstka :)

Tj. w zależności od potrzeb możesz mieć dwa API:

  • /api/me i pochodne, np. /api/me/animals, /api/me/preferences
  • /api/animals i informację o id użytkownika, tj. '/api/animals/{id}' oraz szczególny przypadek tj. '/api/animals/me' - przy czym ten endpoint jest akceptowalny tylko i wyłącznie gdy id nie jest dowolnym stringiem

W takim jednoaplikacyjnym świecie wolę jednak opcję pierwszą, głównie ze względu na prostotę zabezpieczenia czegoś takiego (np. każdy zalogowany użytkownik ma dostęp do /api/me/*, natomiast nie każdy użytkownik ma dostęp do /api/animals/* - w przypadku drugim trzeba dorzucać za każdym razem wyjątek na te nieszczęsne /me.
W przypadku gdy API jest wystawione gdzieś na zewnątrz dla wielu różnych aplikacji wybrałbym opcję drugą - jest to jednak prostsze z punktu widzenia użytkownika API, a /api/me/... zostawił tylko i wyłącznie dla rzeczy stricte niebiznesowych.

0

Poleci ktoś jakąś konkretną książkę do rest api, gdzie właśnie tego typu problemy są poruszane?

0
Xorxorxor napisał(a):

Poleci ktoś jakąś konkretną książkę do rest api, gdzie właśnie tego typu problemy są poruszane?

Weź się czymś pożytecznym zajmij :D

Porusz taką sprawę jak będziesz miał apke z 30 endpointam których nie umiesz nazwać. Teraz lepiej się zajmij rozwijaniem się w programowaniu w ogóle, a nie tym jak nazwać swoje dwa endpointy.

Np napisz testy do nich albo zrób dokumentację.

2

Generalnie to bym wyszedł od tego, kto ma tych endpointów używać, bo jeśli Twój frontend, to powinieneś zacząć od konsumenta. Wtedy zobaczyłbyś, że endpoint pt. /animals nie ma zbyt wielkiego sensu, ponieważ nie ma np. stronicowania, a za chwilę pojawi się potrzeba wyszukiwania np. po nazwie. Zacznij od konsumenta, jeśli możesz.

Jeśli projektujesz jakieś ogólne API dla świata, to możesz wzorować się np. na GitHub albo Allegro - możesz na ich stronach poczytać, jakimi zasadami kierują się projektując API. Tutaj trzeba szczególnie przestrzegać dobrych praktyk i wprowadzać zmiany w sposób przewidywalny, bo nie możesz sobie np. od tak podmienić urla czy pola.

0

Ja bym zrobił tak:

/api/animals
/api/users/current/animals do zwierzątek obecnego użytkownika

Jeśli chodzi o me to raczej my, me to można łyżkę podać...

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