• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

[Code-review] web app w spirng boot do nauki języków

VPS Starter Arubacloud
0 głosów
488 wizyt
pytanie zadane 13 kwietnia 2018 w Nasze projekty przez miro Pasjonat (23,870 p.)

Bardzo proszę o code review  i kilka wskazówek czy w dobrym kierunku idę, jako że jest to moja pierwsza aplikacja w springu. 

Co to za aplikacja: 
Głównym zadaniem aplikacji jest porównywanie dużego tekstu z tekstem przetłumaczonym (najlepiej przez tłumacza), tak aby łatwiej analizowało się zdanie po zdaniu. Dzięki możliwości tworzenia konta możemy nasz tekst zapisać, otagować i tworzyć własny słownik. W przyszłości dodam audio do słówek i grę memo. 

Jakie technologie:
Spring Boot, Spring MVC, Spring Security, Java 8, Hibernate, Posgres database, Project Lombok, thymeleaf, Bootstrap 4. Niestety nie mam jeszcze testów, za dużo robiłem zmian 

Na co zwrócić uwagę: 
Oprócz sugestii odnośnie błędów i dobrych praktyk w springu, proszę szczególnie zwrócić uwagę na security. Nie wiem jak dobrze zabezpieczyć materiały innych użytkowników przed innym zalogowanym użytkownikiem. Mój problem opisałem w tym poście  jednak nikt nie udzielił odpowiedzi. Chciałem rozwiązać to przez sprawdzanie w service czy dany zasób jest własnością zalogowanego użytkownika, jednak na razie wstrzymałem się z tym. Chciałem skupić się na back-endzie, jednak z chęcią przyjmę inne uwagi, szczególnie JS który edytuje tabelę. 

Linki do aplikacji i kodu
https://mighty-castle-53559.herokuapp.com/
https://github.com/amiroslaw/languide
Aplikacja stoi na darmowym hostingu więc czasami trzeba poczekać, aby się wybudziła i uruchomiła :( 

4 odpowiedzi

+1 głos
odpowiedź 13 kwietnia 2018 przez Wiciorny Ekspert (269,100 p.)

W oparciu o jakie źródło pisałeś kod w dużej mierze? 

Primo: jeśli tworzysz warstwę service:

- w niej umieszczasz tylko service i implementacje Repozytoriów ( jeśli masz warstwę Repozytorium, to tutaj znajduje się zarówno interfejs obsługi modeli jak i samego serwisu )- więc z tego co zauważyłem nie jest to czytelne i intuicyjne, że interfejsy masz w warstwie serwisu . Przeniósł bym to do repo 

- troszkę spaghetti code jeśli chodzi o tworzenie  Mapowań HTTP : 

do poczytania: https://blog.philipphauer.de/restful-api-design-best-practices/

https://restfulapi.net/resource-naming/

- ogólnie nie jest źle, bo trudno " do tego intuicyjnie podchodzić z wyprzedzeniem" ale warto mieć na uwodzę  na przyszłość 

- nie wiem jak u Ciebie z konwencją nazewnictwa - czy stosujesz tą SPRINGOWĄ, ale np przy pobieraniu  " wszystkich elementów, jakiejś kolekcji nazwy REST powinny być np  

Collection<?> findAll() 

 

komentarz 13 kwietnia 2018 przez miro Pasjonat (23,870 p.)
Dziękuję za odpisanie i poświęcenie czasu.
Korzystałem z wielu materiałów takich jak baeldung.com czy spring.io. Jednak tamte materiały opierają się głównie na aplikacjach typu hello world. Dużo pomogły mi kursy, których kod można znaleźć na:
- https://github.com/cfaddict/spring-boot-intro  
- https://github.com/springframeworkguru/spring5-recipe-app

Trochę przeglądałem github jednak nie znalazłem nic sensownego. Jakbyś miał jakieś linki do projektów, które są dobrze napisane a zarazem nie za duże, aby w miarę można było się połapać, byłbym wdzięczny.

Wybacz trochę nie zrozumiałem pierwszego punktu z serwisami. Czyli powinienem przenieść interfejsy serwisów do paczki z repository? Gdzie umieszczać logikę,  w serwisach?

Wielkie dzięki za linki do artykułów. Ja nie będę wystawiać api więc uznałem, że łatwiej będzie obsługiwać GETem (jak to robili z kursów, HTML nie ma put, delete..). Usunąłem też status code ponieważ przez to miałem jakiś błąd. Nie tworzyłem uslów z pełną hierarchią, bo trudno mi było generować linki w Thymeleaf. Nie wiem czy takie coś jest dozwolone w aplikacjach web. Zmienię nazwy u url na mnogie. Jak na razie to mam taki pattern (oprócz article który jest bardziej skomplikowany):
/user/{username}/notebooks → list all notebook
/user/{username}/notebook    POST notebookform
/user/{username}/notebook/id/delete    get
/user/{username}/notebook/id/update    get     zwraca notebookform

Dobra uwaga z nazewnictwem, rozjechało mi się bardzo. Pytanie tylko jak bardzo ma być spójne pomiędzy repository, service a controller? W repository update robi metoda save (z CrudRepository), co jak chcesz naprawdę zrobić update (gdy np. w save dodawaliśmy datę stworzenia). W kontrolerze też ma być nazwana metoda findById zwracająca widok artykułu?
+1 głos
odpowiedź 13 kwietnia 2018 przez mbabane Szeryf (79,280 p.)

Z dobrych praktyk to to:

   @Override
    public List<Article> findPublicArticles() {
        List<Article> allArticles = (List<Article>) articleRepository.findAll();
        if (allArticles.isEmpty()) return new ArrayList<>();
        return allArticles.stream().filter(article -> !article.isHidden()).collect(Collectors.toList());
    }

Nie rób czegoś takiego, że pobierasz z bazy wszystkie elementy i flirtujesz je z poziomu javy (co będzie gdy danych będzie 100 000? Aplikacja siądzie na poziomie komunikacji z bazą). Powyższe rozwiązanie można bardzo łatwo uzyskać pisząc zapytanie w JPQL'u:

SELECT a FROM Article a WHERE a.hidden = false; --bądź true

Bardzo ważna cechą bazy danych (poza trzymaniem danych) jest to że jej silnik jest tak optymalizowany, żeby operacje na danych robione były najszybciej jak to się da i należy z tego korzystać tzn. jeśli coś da się zrobić z danymi na bazie (wszelkie filtracje, wyszukiwania) to trzeba to robić na poziomie bazy danych.

komentarz 13 kwietnia 2018 przez NIMuser Stary wyjadacz (11,030 p.)
To ciekawe co piszesz, w ASP.net czy Django budujesz queryset / filtry a baza jest odpytywana dopiero w ostatnim momencie (gdy już ustalisz dokładnie to czego szukasz).
komentarz 13 kwietnia 2018 przez miro Pasjonat (23,870 p.)

Dziękuję za odpowiedź. Tak się domyślałem, że filtrowanie z bazy danych jest lepsze, nawet później dodawałem query methods lub poprzez normalne zapytania SQL. Czy mógłbym Twoje zapytanie zastąpić właśnie przez query method w repository?

List<Article> findAllByHidden();

   Przejrzę kod i zobaczy gdzie jeszcze mógłbym zamienić filtrowanie.  

Mógłbyś się odnieść do mojego problemu z zabezpieczeniem zasobów przed innym zalogowanym użytkownikiem. Np. ktoś zalogowany mógłby zobaczyć kogoś innego artykuł przez zgadywanie id artykułu. Czy jest jakiś sposób na w spring security? Czy raczej wszystkie metody typu deleteById, findById w serwisie zamienić na np. deleteByIdAndUser? A potem wykonywać takie zapytania do bazy danych.

 

komentarz 13 kwietnia 2018 przez mbabane Szeryf (79,280 p.)

 NIMuser

W sumie to masz trochę racji bo zdaje się, że w Springu-data można to napisać tak:

public interface ArticleRepository extends JpaRepository<Article, Long>
{
    public List<Article> findAllByHiddenFalse();
}

Oczywiście to jest prosty przypadek i bardziej zaawansowane rzeczy trzeba już w JPQL'u robić.

 

miro

Przykro mi ale security jeszcze słabo znam i na dzień dzisiejszy rozwiązał bym to poprzez zwykle sprawdzanie właściciela artykułu.

komentarz 13 kwietnia 2018 przez NIMuser Stary wyjadacz (11,030 p.)

Pewnie możliwości jest wiele, ale znalazłem coś takiego:

...W pliku: src/main/java/.........../WebSecurityConfig.java

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

Podobny przykład (fragment):

http
  .httpBasic().and()
  .authorizeRequests()
    .antMatchers(HttpMethod.POST, "/employees").hasRole("ADMIN")
    .antMatchers(HttpMethod.PUT, "/employees/**").hasRole("ADMIN")
    .antMatchers(HttpMethod.PATCH, "/employees/**").hasRole("ADMIN");

Zapis MZ jest raczej zrozumiały.

Zobacz: https://stackoverflow.com/questions/39158941/spring-security-any-request-needs-to-be-authorized-and-a-special-post-request-n

Więcej:

https://spring.io/guides/gs/securing-web/

https://docs.spring.io/spring-security/site/docs/current/reference/html5/

komentarz 13 kwietnia 2018 przez miro Pasjonat (23,870 p.)

 NIMuser np. ten artykuł z srping.io oraz ten post w SO nie rozwiązuje problemu. Każdy user będzie mieć role USER, .authenticated() sprawdza czy jesteś zalogowany. Co zrobić właśnie w przypadku jak jesteś zalogowany i chcesz zmienić coś  należącego do innego użytkownika. Jak na razie widzę tylko dwa rozwiązania albo przez bazę jak wcześniej omawialiśmy, albo generować wszędzie linki z odpowiednią nazwą użytkownika, co w połowie wprowadziłem w moim projekcie. 

@PreAuthorize("#userName == authentication.name")
@GetMapping("/user/{userName}/notebook/{id}/delete")

Jednak trochę trudno mi generować w thymeleaf odpowiednie URL. 

0 głosów
odpowiedź 13 kwietnia 2018 przez NIMuser Stary wyjadacz (11,030 p.)
Za cienki jestem w uszach żeby ocenić jakość kodu w Springu....Ale cieszę się, że nie wszyscy uciekli do RoR, Django, Zend i Node.js, a są tacy co jeszcze męczą się w Springu! :)

Mam wrażenie, że wymyśliłeś pomysł strony trochę na siłę (komu to się przyda?), trzeba było wziąć tradycyjnie bloga, etc ;)

Powiedz, na ile Boot przyspieszył Ci opracowanie projektu? Mogę się mylić, ale spora część kodu wygląda na automatycznie wygenerowaną.

Z jakiej bazy tu korzystasz? (czy w springu można użyć sqlite ?)

Czy ciężki jest deployment? Widzę że tu wybrałeś heroku, jaki serwer aplikacyjny został wybrany?
1
komentarz 13 kwietnia 2018 przez miro Pasjonat (23,870 p.)
edycja 13 kwietnia 2018 przez miro

Za cienki jestem w uszach żeby ocenić jakość kodu w Springu....Ale cieszę się, że nie wszyscy uciekli do RoR, Django, Zend i Node.js, a są tacy co jeszcze męczą się w Springu! :)

Jest dużo ofert pracy więc trochę osób się meczy ;)

Mam wrażenie, że wymyśliłeś pomysł strony trochę na siłę (komu to się przyda?), trzeba było wziąć tradycyjnie bloga, etc ;)

Może nie zrozumiałeś idei tej strony. Sprawdź artykuły z tagu david-polanddavid.blogspot.com lub zaloguj się i dodaj dłuższy tekst. Mi się przyda taka aplikacja i znajomym których uczę polskiego. Po drugie najważniejsze było dla mnie poznanie spinga. Bloga już posiadam smiley

Powiedz, na ile Boot przyspieszył Ci opracowanie projektu? Mogę się mylić, ale spora część kodu wygląda na automatycznie wygenerowaną.


Niestety nie mogę Tobie odpowiedzieć bo sporą część czasu zajęła mi sama nauka. Faktycznie kod wygląda jakby był wygenerowany frown Chyba wynika to z tego, że niektóre rzeczy robi się prawie identycznie, albo zgodnie z jakimiś praktykami. 

 Z jakiej bazy tu korzystasz? (czy w springu można użyć sqlite ?)

Tak można użyć sqlite, ale np. w spring boot są wspierane inne bazy. Ja lokalnie używam H2, a na heroku postgres.   

Czy ciężki jest deployment? Widzę że tu wybrałeś heroku, jaki serwer aplikacyjny został wybrany?

Darmowy serwer java, jeśli o to Tobie chodzi. Deployment na heroku jest bardzo prosty, ale miałem kilka problemów z podmianą bazy danych. 

komentarz 13 kwietnia 2018 przez NIMuser Stary wyjadacz (11,030 p.)
Dzięki za dokładne odpowiedzi!

Właśnie wyczytałem, że na developmencie można mieć "bazę" w pamięci, czyli bez zapisu na dysk.

Gratuluje projektu - jak na pierwszy, to wygląda interesująco!
0 głosów
odpowiedź 13 kwietnia 2018 przez jeremus Maniak (59,720 p.)
nie wnikałem jeszcze w kod.

ale poklikałem trochę na tej tabelce

dlaczego kliknięcie prawym klawiszem myszy usuwa treść? - to zamierzone ?

po usunięciu wszystkich wierszy wstawić już nowego nie można - jest tylko nagłówek tabeli - trochę można się pogubić
komentarz 13 kwietnia 2018 przez miro Pasjonat (23,870 p.)

dlaczego kliknięcie prawym klawiszem myszy usuwa treść? - to zamierzone ?

Tak jest to zamierzone, ponieważ jak weźmiesz duży artykuł i jego tłumaczenie to nawet jak będziesz mieć profesjonalne tłumaczenie to ten tekst się może rozjechać, np. przy skrótowcach (e.g./np.). Rozbijam duży tekst na zdania poprzez chociażby kropki.   

po usunięciu wszystkich wierszy wstawić już nowego nie można - jest tylko nagłówek tabeli - trochę można się pogubić

Tak jak usuniesz wszystkie wiersze to nie dodasz nowego. Dzięki za uwagę. 

Podobne pytania

0 głosów
2 odpowiedzi 142 wizyt
pytanie zadane 5 października 2017 w Java przez Jonki Dyskutant (8,180 p.)
+1 głos
1 odpowiedź 1,111 wizyt
0 głosów
1 odpowiedź 144 wizyt
pytanie zadane 27 maja 2018 w Nasze projekty przez kamil159 Nowicjusz (180 p.)

92,451 zapytań

141,261 odpowiedzi

319,073 komentarzy

61,853 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...