Co powinien w tym przypadku zwrócić Optional?

0

Hej,

tworzę sobie Restowy backend w Javie i zastanawiam się jak rozwiązać sprawę zwracania obiektu w kontrolerze. Może pokażę to na przykładzie kodu.
Mam kontroler, a w nim metodę, która powinna zwrócić na front obiekt o id podanym w ścieżce URI. Do kontrolera mam wstrzyknięte repozytorium wykorzystujące Spring Data. Wbudowana metoda findById zwraca obiekt typu Optional. No więc mój kontroler wygląda tak:

@GetMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<DiskType> getDisk(@PathVariable long id){
        return new ResponseEntity<>(diskTypeRepository.findById(id).orElseGet("????"), new HttpHeaders(), HttpStatus.OK);
    }

I co w takiej sytuacji wrzucić do nawiasu po ```orElseGet``, jeżeli findById zwróci null? Chyba, że w ogóle zabieram się za to od złej strony? To mój pierwszy serwis Restowy, więc jestem trochę zagubiony i staram się zrobić wszystko tak jak się robić powinno

2

Powinienes wtedy zwracać nic jako odpowiedź i kod 404, czyli wykorzystać czyli w coś ala:

diskTypeRepository.findById(id).map(this::diskToResponseEntity).orElse(new ResponsEntity(HttpStatus.NOT_FOUND));
0
scibi92 napisał(a):

Powinienes wtedy zwracać nic jako odpowiedź i kod 404, czyli wykorzystać czyli w coś ala:

diskTypeRepository.findById(id).map(this::diskToResponseEntity).orElse(new ResponsEntity(HttpStatus.NOT_FOUND));

Nic, czyli po prostu... null?

0

Osobiście użyłbym @ControllerAdvice ale o tym musisz trochę poczytać. Wtedy można by w kontrolerze zawsze zwracać status oczekiwany czyli np. 200 podczas pobrania po id. Logikę typu orElse przeniósłbym do warstwy serwisu, który by zapewnił nie zwrócenie null

0
victordeleco2 napisał(a):

Osobiście użyłbym @ControllerAdvice ale o tym musisz trochę poczytać. Wtedy można by w kontrolerze zawsze zwracać status oczekiwany czyli np. 200 podczas pobrania po id. Logikę typu orElse przeniósłbym do warstwy serwisu, który by zapewnił nie zwrócenie null

Wydzielam zatem to do serwisu. Jednak problem dalej pozostaje, bo orElse musi cos zwrocic w przypadku pustego Optionala. Co wiec zwracac wtedy do kontrolera? Pusty obiekt? Czy cos innego? Najlepiej tak, zeby mogl zareagowac na taka sytuacje np. kodem 404 w ResponseEntity.

0
xxx_xx_x napisał(a):

Dlaczego nie skorzystasz z tego?
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html#of-java.util.Optional-

Skorzystam i przetestuje za chwile, nie skorzystalem wczesniej, bo nie wiedzialem o jej istnieniu. Powinienem chyba czesciej przekopywac dokumentacje, bo rzeczywiscie czesto w banalny sposob mozna dzieki niej rozwiazac swoje problemy

0

Możesz np. stworzyć własny wyjątek DiskNotFoundException a wtedy robisz .orElseThrow(()-> new DiskNotFoundException("Disk with: " + id "not found") i w klasie z @ControllerAdvice to łapiesz i zwracasz 404 a w body np. jego tresc.

1

@Belka: nie słuchaj rad @victordeleco2. Wyjatek nie ma tu żadnego sensu i to bardzo złe rozwiązanie ! Wyjątki sa do sytuacji wyjątkowych (np. błąd odczytu z pliku), ale próba pobrania czegoś co nie istnieje jeśli rzeczywiście może nie istniec to nie jest wyjątkowa sytuacja..

0
scibi92 napisał(a):

@Belka: nie słuchaj rad @victordeleco2. Wyjatek nie ma tu żadnego sensu i to bardzo złe rozwiązanie !

Sprobuje rozwiazania z ResponseEntity.of, bo wyglada ciekawie, a jak juz korzystam z tego Springa to wykorzystam jego mozliwosci. Ciekawi mnie jednak jedna kwestia w Twoim rozwiazaniu.
Zaproponowales tam cos takiego ...diskTypeRepository.findById(id).map(**this::diskToResponseEntity**.....

Rozumiem, ze to mialaby byc funkcja konwertujaca obiekt DiskType na ResponseEntity<DiskType> z kodem http 200. Czy taka funkcja powinna byc zaimplementowana w klasie obiektu domenowego (w sensie obiektu @Entity)?

3

@Belka: no nie, bo encja bazodanowa nie powinna miec nic wspólnego z protokołem HTTP... co więcej nawet takiej encji nie powineneś zwracac bezpośrednio

0
scibi92 napisał(a):

@Belka: no nie, bo encja bazodanowa nie powinna miec nic wspólnego z protokołem HTTP... co więcej nawet takiej encji nie powineneś zwracac bezpośrednio

Sorry za nawałnicę pytań, ale w takim razie rozumiem, że każdy obiekt, który będzie reprezentowany na froncie (nie znam frontendu kompletnie, bo robi go inna osoba, lecz od strony backendu chcę wykonać swoje zadanie jak najlepiej potrafię) powinien być wysłany w JSON-ie już w takiej formie, w jakiej ma być wyświetlany?
Innymi słowy - jeżeli pobieram listę jakichś obiektów to powinienem się pozbyć kolumn id, ManyToMany itd, stąd konieczność DTO?

1

Do frontendu wysyłasz minimum danych, które pozwalają na wykonanie zadania.
Nie jest to zwykle dokładnie to co frontend wyświetla, na pewno będą tam jakieś metadane oraz frontend sam te dane odpowiednio sformatuje.

0

Dzięki wszystkim za pomoc. Nie zalewając Was dalej pytaniami lecę przetrawić wiedzę i popraktykować ;)

0

... każdy obiekt, który będzie reprezentowany na froncie (...) powinien być wysłany w JSON-ie już w takiej formie, w jakiej ma być wyświetlany?

Niekoniecznie, choć często tak wychodzi. Pisząc backend, projektujesz API. To "I" oznacza interfejs, można by powiedzieć kontrakt. I ten kontrakt określa na przykład, że GET na /admin/all_users zwróci listę wszystkich użytkowników w systemie, a każdy użytkownik reprezentowany jest przez jeden obiekt z polami username i gender. To, jak to będzie wyświetlane na froncie, generalnie nie powinno mieć aż takiego znaczenia. Dziś to będzie lista ul, a jutro przyjdzie szef i każe pogrupować użytkowników na froncie według płci w osobnych zakładkach. Czy będzie potrzeba zmienić API na takie, co grupuje po stronie backendu i wysyła już pogrupowane obiekty? To zależy. Może tych obiektów jest mało i frontendowiec ma luzy, więc zakodzi sobie to w JS. A może jest ich milion i trzeba zrobić GROUP BY na poziomie bazy, bo inaczej będzie wolno działało.

A odnośnie wysyłania całych encji bazodanowych na front - tak, jest to zwykle słaba praktyka, bo wystawiasz na ślepo wszystko co masz w bazie (no chyba że nawalisz adnotacji z jacksona i nie natrafisz na circular reference). Przykład - może nie trzeba wysyłać na front hasha hasła użytkownika (albo hasła xD), mimo że jest takie pole w bazie.

0

@kelog do tego backend to często api dla innych urządzeń, nie tylko www. Na sprzętach mobilnych często to co wysyła backend musi zostać przemielone na różne osobne ekrany, inaczej formatowane ze względu na preferencje w telefonie itp.

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