• 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

0 głosów
170 wizyt
pytanie zadane 13 kwietnia w Nasze projekty przez miro Stary wyjadacz (14,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 przez Wiciorny Maniak (66,200 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 przez miro Stary wyjadacz (14,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 przez mbabane Maniak (57,520 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 przez NIMuser Stary wyjadacz (10,140 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 przez miro Stary wyjadacz (14,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 przez mbabane Maniak (57,520 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 przez NIMuser Stary wyjadacz (10,140 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 przez miro Stary wyjadacz (14,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 przez NIMuser Stary wyjadacz (10,140 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 przez miro Stary wyjadacz (14,870 p.)
edycja 13 kwietnia 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 przez NIMuser Stary wyjadacz (10,140 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 przez jeremus Maniak (58,020 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 przez miro Stary wyjadacz (14,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 70 wizyt
pytanie zadane 5 października 2017 w Java przez Jonki Dyskutant (8,220 p.)
+2 głosów
1 odpowiedź 491 wizyt
0 głosów
1 odpowiedź 66 wizyt
pytanie zadane 27 maja w Nasze projekty przez kamil159 Nowicjusz (180 p.)
Porady nie od parady
Zadając pytanie postaraj się o poprawną pisownię i czytelne formatowanie tekstu.Kompozycja

57,543 zapytań

102,549 odpowiedzi

211,501 komentarzy

29,371 pasjonatów

Przeglądających: 278
Pasjonatów: 19 Gości: 259

Motyw:

Akcja Pajacyk

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

Oto dwie polecane książki warte uwagi. Pełną listę znajdziesz tutaj.

...