Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00239 005426 12765054 na godz. na dobę w sumie
Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW - książka
Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW - książka
Autor: Liczba stron: 272
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-283-2347-6 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> java - programowanie
Porównaj ceny (książka, ebook, audiobook).
Wszyscy jesteśmy świadkami dynamicznego rozwoju branży aplikacji internetowych. Projektanci i programiści muszą jeszcze szybciej tworzyć coraz doskonalsze i atrakcyjniejsze aplikacje, a następnie błyskawicznie udostępniać je użytkownikom, przy dość ograniczonym budżecie. Platforma Spring Boot i środowiska chmurowe pozwalają sprostać tym wymaganiom: niezwykłe aplikacje można tworzyć i przekazywać w rekordowym tempie, w dodatku wyposażone w tak istotne funkcjonalności jak internacjonalizacja, sesje rozproszone, logowanie społecznościowe, wielowątkowość i wiele innych.

Jeśli programujesz w Javie, choć trochę znasz platformę Spring i chcesz tworzyć użyteczne oraz nowoczesne aplikacje WWW, masz w ręku właściwą książkę. Ten podręcznik w niezwykle praktyczny sposób podchodzi do zagadnienia budowy skomplikowanych aplikacji z wykorzystaniem nowoczesnych technologii.

Podczas lektury poszczególnych rozdziałów będziesz mógł od podstaw przyjrzeć się konstruowaniu w pełni działającej aplikacji WWW, a potem spróbować własnych sił w tej dziedzinie, z wykorzystaniem internacjonalizacji, weryfikacji formularzy oraz obsługi rozproszonych sesji i pamięci podręcznej. Dowiesz się również, jak porządnie przetestować aplikację i opublikować ją w internecie.

W tej książce znajdziesz:

Programuj jak mistrz — odkryj Spring MVC!


Geoffroy Warin — programuje od dziecka. Jest gorącym orędownikiem tworzenia otwartego kodu. Niezachwianie wierzy w ideę Software Craftsmanship (osiągania mistrzostwa w programowaniu). Jest uznanym specjalistą w dziedzinie budowania biznesowych aplikacji WWW w języku Java i entuzjastą platform Groovy oraz Spring. Po godzinach prowadzi bloga, jest szkoleniowcem i autorem oraz współautorem książek.
Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: Mastering Spring MVC 4 Tłumaczenie: Andrzej Watrak ISBN: 978-83-283-2347-6 Copyright © 2015 Packt Publishing First published in the English language under the title ‘Mastering Spring MVC 4 – (9781783982387)’. Polish edition copyright © 2016 by Helion S.A. All rights reserved. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/smvc4p.zip Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/smvc4p Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis tre(cid:258)ci O autorze O korektorach merytorycznych Przedmowa Rozdzia(cid:239) 1. B(cid:239)yskawiczne tworzenie aplikacji Spring Rozpocz(cid:218)cie pracy w (cid:258)rodowisku Spring Tool Suite Rozpocz(cid:218)cie pracy w (cid:258)rodowisku IntelliJ Rozpocz(cid:218)cie pracy w serwisie start.Spring.io Rozpocz(cid:218)cie pracy za pomoc(cid:200) wiersza polece(cid:241) Pierwsze kroki Kompilowanie kodu za pomoc(cid:200) narz(cid:218)dzia Gradle Chc(cid:218) zobaczy(cid:202) kod! Spring Boot od wewn(cid:200)trz Dyspozytor i konfiguracja elementów aplikacji Interpreter widoków, zasoby statyczne i ustawienia regionalne Konfiguracja obs(cid:239)ugi b(cid:239)(cid:218)dów i kodowania znaków Konfiguracja wbudowanego serwletu kontenera serwera (Tomcat) Port HTTP Konfiguracja protoko(cid:239)u SSL Inne opcje konfiguracyjne Podsumowanie Rozdzia(cid:239) 2. Tajniki architektury MVC Architektura MVC Krytyka architektury MVC i dobre praktyki Anemiczny model domeny Informacje ze (cid:283)róde(cid:239) Platforma MVC 1-0-1 11 12 15 19 20 25 26 26 27 28 32 34 35 38 40 42 44 44 45 46 47 47 48 48 50 50 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW Szablony Thymeleaf Twoja pierwsza strona Architektura platformy Spring MVC Serwlet DispatcherServlet Przekazywanie danych do widoku J(cid:218)zyk Spring Expression Language U(cid:285)ycie parametru przy odczytywaniu danych Dosy(cid:202) ju(cid:285) „Witaj, (cid:258)wiecie!”, odczytujmy tweety! Rejestracja aplikacji Zastosowanie projektu Spring Social Dost(cid:218)p do serwisu Twitter Strumienie i funkcje lambda w Java 8 Styl material design i biblioteka WebJars Uk(cid:239)ady stron Poruszanie si(cid:218) po witrynie Punkt kontrolny Podsumowanie Rozdzia(cid:239) 3. Obs(cid:239)uga formularzy i z(cid:239)o(cid:285)onych adresów URL Strona profilu — formularz Weryfikacja danych Dostosowanie komunikatów o b(cid:239)(cid:218)dach Niestandardowe adnotacje do weryfikacji danych Internacjonalizacja Zmiana ustawie(cid:241) regionalnych T(cid:239)umaczenie tekstów aplikacji Lista w formularzu Weryfikacja danych po stronie klienta Punkt kontrolny Podsumowanie Rozdzia(cid:239) 4. (cid:146)adowanie plików i obs(cid:239)uga b(cid:239)(cid:218)dów (cid:146)adowanie plików Umieszczanie obrazu w odpowiedzi na zapytanie Zarz(cid:200)dzanie konfiguracj(cid:200) (cid:239)adowania plików Wy(cid:258)wietlenie za(cid:239)adowanego obrazu Obs(cid:239)uga b(cid:239)(cid:218)dów (cid:239)adowania plików T(cid:239)umaczenia komunikatów o b(cid:239)(cid:218)dach Zapisywanie profilu u(cid:285)ytkownika w sesji W(cid:239)asne strony z komunikatami o b(cid:239)(cid:218)dach Zmienne tablicowe w adresach URL Wszystko razem Punkt kontrolny Podsumowanie 4 51 52 54 54 55 56 56 58 58 60 60 62 63 66 67 71 72 73 73 80 82 85 85 87 89 91 94 96 96 99 99 104 104 107 108 112 112 116 117 121 128 129 Poleć książkęKup książkę Spis tre(cid:286)ci Rozdzia(cid:239) 5. Tworzenie aplikacji w stylu REST Czym jest styl REST? Model dojrza(cid:239)o(cid:258)ci Richardsona Poziom 0 — HTTP Poziom 1 — zasoby Poziom 2 — metody HTTP Poziom 3 — kontrolki hipermediów Wersje interfejsu API Przydatne kody HTTP Klient jest królem Diagnostyka interfejsu REST API Rozszerzenia przegl(cid:200)darek wy(cid:258)wietlaj(cid:200)ce format JSON Klient REST w przegl(cid:200)darce Narz(cid:218)dzie httpie Dostosowanie odpowiedzi JSON Interfejs API do zarz(cid:200)dzania zasobami u(cid:285)ytkowników Kody stanu i obs(cid:239)uga wyj(cid:200)tków Zwrot kodu stanu za pomoc(cid:200) obiektu ResponseEntity Zwrot kodów stanu za pomoc(cid:200) wyj(cid:200)tków Dokumentowanie interfejsu za pomoc(cid:200) platformy Swagger Tworzenie odpowiedzi XML Punkt kontrolny Podsumowanie Rozdzia(cid:239) 6. Zabezpieczanie aplikacji Podstawowe uwierzytelnienie Upowa(cid:285)nieni u(cid:285)ytkownicy Uprawnione adresy URL Znaczniki bezpiecze(cid:241)stwa w szablonie Thymeleaf Formularz logowania Uwierzytelnienie przez Twitter Konfiguracja uwierzytelnienia spo(cid:239)eczno(cid:258)ciowego Obja(cid:258)nienia do kodu Rozproszone sesje Protokó(cid:239) SSL Generowanie certyfikatu z w(cid:239)asnym podpisem Jeden kana(cid:239) Dwa kana(cid:239)y Za bezpiecznym serwerem Punkt kontrolny Podsumowanie Rozdzia(cid:239) 7. Zero ryzyka — testy jednostkowe i integracyjne Dlaczego powinienem testowa(cid:202) swój kod? Jak powiniene(cid:258) testowa(cid:202) swój kod? Programowanie zorientowane na testy 131 131 132 132 132 133 134 135 136 137 139 139 139 139 139 144 147 148 149 153 154 156 157 159 159 160 163 164 165 170 170 174 176 178 179 179 180 181 181 182 183 183 184 185 5 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW Testy jednostkowe Narz(cid:218)dzia odpowiednie do zadania Testy integracyjne Twój pierwszy test jednostkowy Imitacje i atrapy Imitowanie klas przy u(cid:285)yciu narz(cid:218)dzia Mockito Tworzenie atrap klas podczas testów Trzeba u(cid:285)ywa(cid:202) imitacji czy atrap? Testy jednostkowe kontrolerów REST Testowanie uwierzytelnienia Tworzenie testów integracyjnych Konfiguracja systemu Gradle Pierwszy test FluentLenium Obiekty stron w bibliotece FluentLenium Tworzenie testów w j(cid:218)zyku Groovy Testy jednostkowe z wykorzystaniem biblioteki Spock Testy integracyjne z wykorzystaniem biblioteki Geb Obiekty stron w bibliotece Geb Punkt kontrolny Podsumowanie Rozdzia(cid:239) 8. Optymalizacja zapyta(cid:241) Produkcyjny profil aplikacji Kompresja gzip Kontrola pami(cid:218)ci podr(cid:218)cznej Pami(cid:218)(cid:202) podr(cid:218)czna aplikacji Uniewa(cid:285)nianie danych w pami(cid:218)ci podr(cid:218)cznej Rozproszona pami(cid:218)(cid:202) podr(cid:218)czna Metody asynchroniczne Tagi ETag Protokó(cid:239) WebSocket Punkt kontrolny Podsumowanie Rozdzia(cid:239) 9. Udost(cid:218)pnianie aplikacji w chmurze Wybór operatora us(cid:239)ug chmurowych Cloud Foundry OpenShift Heroku Udost(cid:218)pnienie aplikacji w us(cid:239)udze Pivotal Web Services Instalacja narz(cid:218)dzi konsolowych Cloud Foundry Z(cid:239)o(cid:285)enie aplikacji Aktywacja us(cid:239)ugi Redis Udost(cid:218)pnienie aplikacji w us(cid:239)udze Heroku Instalacja narz(cid:218)dzi Konfiguracja aplikacji Profil Heroku 6 186 187 187 188 191 191 193 195 195 201 202 202 204 209 212 212 215 217 220 221 223 223 224 224 226 231 232 233 237 241 244 244 245 245 246 246 247 247 247 248 252 253 253 254 255 Poleć książkęKup książkę Spis tre(cid:286)ci Uruchomienie aplikacji Aktywacja us(cid:239)ugi Redis Ulepszanie aplikacji Podsumowanie Rozdzia(cid:239) 10. Nie tylko Spring Web Platforma Spring Core (rdze(cid:241)) Execution (uruchamianie) Data (dane) Inne ciekawe projekty Wdro(cid:285)enie Platforma Docker Aplikacje jednostronicowe Najwa(cid:285)niejsi gracze Przysz(cid:239)o(cid:258)(cid:202) Bezstanowo(cid:258)(cid:202) Podsumowanie Skorowidz 256 258 259 260 261 261 262 262 262 263 263 263 264 265 265 266 266 267 7 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW 8 Poleć książkęKup książkę 4 (cid:146)adowanie plików i obs(cid:239)uga b(cid:239)(cid:218)dów W tym rozdziale umo(cid:285)liwisz u(cid:285)ytkownikom (cid:239)adowanie obrazów do profili. Dowiesz si(cid:218) rów- nie(cid:285), jak obs(cid:239)ugiwa(cid:202) b(cid:239)(cid:218)dy w platformie Spring MVC. (cid:146)adowanie plików Teraz umo(cid:285)liwisz u(cid:285)ytkownikom (cid:239)adowanie obrazów do profili. Opis, jak to zrobi(cid:202), znajduje si(cid:218) w dalszej cz(cid:218)(cid:258)ci rozdzia(cid:239)u, ale teraz upro(cid:258)(cid:202) nieco projekt i utwórz w katalogu templates/profile now(cid:200) stron(cid:218) uploadPage.html: !DOCTYPE html html xmlns:th= http://www.thymeleaf.org xmlns:layout= http://www.ultraq.net.nz/thymeleaf/layout layout:decorator= layout/default head lang= pl title (cid:146)adowanie obrazu /title /head body div class= row layout:fragment= content h2 class= indigo-text center (cid:146)adowanie obrazu /h2 form th:action= @{/upload} method= post enctype= multipart/form-data class= col m8 s12 offset-m2 div class= input-field col s6 input type= file id= file name= file / /div div class= col s6 center Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW button class= btn indigo waves-effect waves-light type= submit (cid:180)name= save th:text= #{submit} Wy(cid:258)lij i class= mdi-content-send right /i /button /div /form /div /body /html Nie ma tu niczego ciekawego poza atrybutem enctype. Plik z obrazem b(cid:218)dzie wysy(cid:239)any metod(cid:200) POST na adres URL upload. Teraz w pakiecie profile obok klasy ProfileController utwórz od- powiedni kontroler: package masterSpringMvc.profile; import org.apache.tomcat.util.http.fileupload.IOUtils; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @Controller public class PictureUploadController { public static final Resource PICTURES_DIR = new (cid:180)FileSystemResource( ./pictures ); @RequestMapping( upload ) public String uploadPage() { return profile/uploadPage ; } @RequestMapping(value = /upload , method = RequestMethod.POST) public String onUpload(MultipartFile file) throws IOException { String filename = file.getOriginalFilename(); File tempFile = File.createTempFile( pic , getFileExtension(filename), PICTURES_DIR.getFile()); try (InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream(tempFile)) { IOUtils.copy(in, out); } 100 Poleć książkęKup książkę Rozdzia(cid:225) 4. • (cid:224)adowanie plików i obs(cid:225)uga b(cid:225)(cid:266)dów return profile/uploadPage ; } private static String getFileExtension(String name) { return name.substring(name.lastIndexOf( . )); } } Pierwsz(cid:200) operacj(cid:200) wykonywan(cid:200) przez powy(cid:285)szy kod jest utworzenie tymczasowego pliku w kata- logu pictures (obrazy), znajduj(cid:200)cym si(cid:218) w g(cid:239)ównym katalogu projektu. Sprawd(cid:283) wi(cid:218)c, czy ten katalog istnieje. W j(cid:218)zyku Java tymczasowy plik posiada unikatowy identyfikator w systemie plików. Usuni(cid:218)cie pliku zale(cid:285)y od u(cid:285)ytkownika. Utwórz w g(cid:239)ównym katalogu projektu katalog pictures, a w nim pusty plik o nazwie .gitkeep, aby katalog by(cid:239) zapisywany w systemie Git. Puste katalogi w systemie Git System Git s(cid:239)u(cid:285)y do zarz(cid:200)dzania plikami i nie ma mo(cid:285)liwo(cid:258)ci zapisywania w nim pustych katalogów. Powszechnie stosowan(cid:200) metod(cid:200) omini(cid:218)cia tego ograniczenia jest stosowanie pustych plików, na przyk(cid:239)ad .gitkeep. W ten sposób zmusza si(cid:218) system do uwzgl(cid:218)dniania katalogu w procesie kontroli wersji projektu. Plik (cid:239)adowany przez u(cid:285)ytkownika jest reprezentowany w kontrolerze przez interfejs Multipart (cid:180)File. Interfejs ten posiada kilka metod zwracaj(cid:200)cych nazw(cid:218) pliku, jego wielko(cid:258)(cid:202) i zawarto(cid:258)(cid:202). Szczególnie interesuje nas metoda getInputStream. Reprezentowany przez ni(cid:200) strumie(cid:241) wej- (cid:258)ciowy zostanie skopiowany do metody fileOutputStream za pomoc(cid:200) metody IOUtils.copy. Pisanie kodu kopiuj(cid:200)cego strumie(cid:241) wej(cid:258)ciowy do wyj(cid:258)ciowego jest do(cid:258)(cid:202) nudne, wi(cid:218)c wygodniej b(cid:218)dzie skorzysta(cid:202) z biblioteki Apache Utils, zawartej w pliku tomcat-embed-core.jar, umiesz- czonym w (cid:258)cie(cid:285)ce classpath. W tej cz(cid:218)(cid:258)ci b(cid:218)dziesz intensywnie wykorzystywa(cid:239) ciekawe funkcjonalno(cid:258)ci platformy Spring i biblioteki NIO wprowadzonej w wersji Java 7: (cid:81) pomocnicz(cid:200) klas(cid:218) String reprezentuj(cid:200)c(cid:200) zasób, który mo(cid:285)na przetwarza(cid:202) na ró(cid:285)ne sposoby; (cid:81) blok instrukcji try...with automatycznie zamykaj(cid:200)cy strumie(cid:241) nawet w przypadku wyst(cid:200)pienia wyj(cid:200)tku, dzi(cid:218)ki czemu nie trzeba ka(cid:285)dorazowo definiowa(cid:202) bloku finally. W powy(cid:285)szym kodzie ka(cid:285)dy plik za(cid:239)adowany przez u(cid:285)ytkownika zostanie skopiowany do ka- talogu pictures. Platforma Spring Boot oferuje kilka w(cid:239)a(cid:258)ciwo(cid:258)ci umo(cid:285)liwiaj(cid:200)cych dostosowanie procesu (cid:239)a- dowania plików. Przyjrzyjmy si(cid:218) klasie MultipartProperties. 101 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW Jej najciekawsze w(cid:239)a(cid:258)ciwo(cid:258)ci to: (cid:81) multipart.maxFileSize, w(cid:239)a(cid:258)ciwo(cid:258)(cid:202) definiuj(cid:200)ca maksymaln(cid:200) wielko(cid:258)(cid:202) (cid:239)adowanego pliku. Przy próbie za(cid:239)adowania wi(cid:218)kszego pliku jest zg(cid:239)aszany wyj(cid:200)tek MultipartException. Domy(cid:258)lna wielko(cid:258)(cid:202) pliku to 1 MB. (cid:81) multipart.maxRequestSize, w(cid:239)a(cid:258)ciwo(cid:258)(cid:202) definiuj(cid:200)ca maksymaln(cid:200) wielko(cid:258)(cid:202) (cid:285)(cid:200)dania wielocz(cid:218)(cid:258)ciowego. Domy(cid:258)lna wielko(cid:258)(cid:202) to 10 MB. Powy(cid:285)sze warto(cid:258)ci domy(cid:258)lne s(cid:200) odpowiednie dla Twojej aplikacji. Po za(cid:239)adowaniu kilku pli- ków katalog pictures b(cid:218)dzie wygl(cid:200)da(cid:239) nast(cid:218)puj(cid:200)co: Zaczekaj! Kto(cid:258) za(cid:239)adowa(cid:239) plik ZIP! A(cid:285) trudno w to uwierzy(cid:202). Trzeba w kodzie kontrolera spraw- dza(cid:202), czy (cid:239)adowane pliki faktycznie reprezentuj(cid:200) obrazy: package masterSpringMvc.profile; import org.apache.tomcat.util.http.fileupload.IOUtils; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import java.io.*; @Controller public class PictureUploadController { public static final Resource PICTURES_DIR = new FileSystemResource( ./pictures ); @RequestMapping( upload ) public String uploadPage() { return profile/uploadPage ; } @RequestMapping(value = /upload , method = RequestMethod.POST) public String onUpload(MultipartFile file, RedirectAttributes redirectAttrs) throws IOException { if (file.isEmpty() || !isImage(file)) { redirectAttrs.addFlashAttribute( error , Niew(cid:239)a(cid:258)ciwy plik. Za(cid:239)aduj plik z obrazem. ); return redirect:/upload ; 102 Poleć książkęKup książkę Rozdzia(cid:225) 4. • (cid:224)adowanie plików i obs(cid:225)uga b(cid:225)(cid:266)dów } copyFileToPictures(file); return profile/uploadPage ; } private Resource copyFileToPictures(MultipartFile file) throws IOException { String fileExtension = getFileExtension(file.getOriginalFilename()); File tempFile = File.createTempFile( pic , fileExtension, PICTURES_DIR.getFile()); try (InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream(tempFile)) { IOUtils.copy(in, out); } return new FileSystemResource(tempFile); } private boolean isImage(MultipartFile file) { return file.getContentType().startsWith( image ); } private static String getFileExtension(String name) { return name.substring(name.lastIndexOf( . )); } } Bardzo proste! Metoda getContentType zwraca informacj(cid:218) o typie pliku w formacie MIME (ang. Multipurpose Internet Mail Extensions — uniwersalne rozszerzenie poczty internetowej), np. image/png, image/jpg itp. Wystarczy wi(cid:218)c sprawdzi(cid:202), czy ci(cid:200)g MIME rozpoczyna si(cid:218) od s(cid:239)owa image. W formularzu zosta(cid:239)a zakodowana obs(cid:239)uga b(cid:239)(cid:218)dów, wi(cid:218)c trzeba rozbudowa(cid:202) stron(cid:218) o jakie(cid:258) ele- menty umo(cid:285)liwiaj(cid:200)ce wy(cid:258)wietlanie komunikatów. Wpisz w pliku uploadPage.html nast(cid:218)puj(cid:200)- ce wiersze tu(cid:285) poni(cid:285)ej tytu(cid:239)u strony: div class= col s12 center red-text th:text= ${error} th:if= ${error} B(cid:239)(cid:200)d (cid:239)adowania pliku /div Przy nast(cid:218)pnej próbie za(cid:239)adowania pliku ZIP pojawi si(cid:218) komunikat o b(cid:239)(cid:218)dzie, jak na poni(cid:285)- szym rysunku: 103 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW Umieszczanie obrazu w odpowiedzi na zapytanie (cid:146)adowane obrazy nie s(cid:200) zapisywane w statycznych katalogach. Aby wy(cid:258)wietli(cid:202) je na stronie trzeba przedsi(cid:218)wzi(cid:200)(cid:202) specjalne (cid:258)rodki. W kodzie strony uploadPage.html tu(cid:285) nad znacznikiem form wpisz poni(cid:285)sze wiersze: div class= col m8 s12 offset-m2 img th:src= @{/uploadedPicture} width= 100 height= 100 / /div Powy(cid:285)szy kod b(cid:218)dzie próbowa(cid:239) pobra(cid:202) obraz z kontrolera. W klasie PictureUploadController utwórz odpowiedni(cid:200) metod(cid:218): @RequestMapping(value = /uploadedPicture ) public void getUploadedPicture(HttpServletResponse response) throws IOException { ClassPathResource classPathResource = new (cid:180)ClassPathResource( /images/anonymous.png ); response.setHeader( Content-Type , URLConnection.guessContentTypeFromName(classPathResource.getFilename())); IOUtils.copy(classPathResource.getInputStream(), response.getOutputStream()); } Powy(cid:285)szy kod b(cid:218)dzie umieszcza(cid:239) bezpo(cid:258)rednio w odpowiedzi obraz z pliku src/main/resources/ images/anonymous.png. To bardzo ciekawy sposób! Je(cid:285)eli ponownie otworzysz stron(cid:218), pojawi si(cid:218) na niej nast(cid:218)puj(cid:200)cy obraz: Rysunek anonimowego u(cid:285)ytkownika znalaz(cid:239)em na stronie http://iconmonstr.com/user-icon i zapisa(cid:239)em go jako obraz o wymiarach 128×128 pikseli w pliku PNG. Zarz(cid:200)dzanie konfiguracj(cid:200) (cid:239)adowania plików W tym momencie warto jest okre(cid:258)li(cid:202) w pliku konfiguracyjnym application.properties katalog dla (cid:239)adowanych obrazów i (cid:258)cie(cid:285)k(cid:218) do rysunku anonimowego u(cid:285)ytkownika. 104 Poleć książkęKup książkę Rozdzia(cid:225) 4. • (cid:224)adowanie plików i obs(cid:225)uga b(cid:225)(cid:266)dów Utwórz pakiet config, a w nim klas(cid:218) PicturesUploadProperties: package masterSpringMvc.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import java.io.IOException; @ConfigurationProperties(prefix = upload.pictures ) public class PictureUploadProperties { private Resource uploadPath; private Resource anonymousPicture; public Resource getAnonymousPicture() { return anonymousPicture; } public void setAnonymousPicture(String anonymousPicture) { this.anonymousPicture = new DefaultResourceLoader().getResource (cid:180)(anonymousPicture); } public Resource getUploadPath() { return uploadPath; } public void setUploadPath(String uploadPath) { this.uploadPath = new DefaultResourceLoader().getResource(uploadPath); } } W tej klasie wykorzystany zosta(cid:239) pakiet ConfigurationProperties platformy Spring Boot, umo(cid:285)li- wiaj(cid:200)cy automatyczne mapowanie w(cid:239)a(cid:258)ciwo(cid:258)ci znalezionych w plikach umieszczonych w (cid:258)cie(cid:285)ce classpath (domy(cid:258)lnie w pliku application.properties), z uwzgl(cid:218)dnieniem typów tych w(cid:239)a(cid:258)ciwo(cid:258)ci. Zwró(cid:202) uwag(cid:218), (cid:285)e zdefiniowana jest metoda z argumentem typu String, ale nic nie stoi na prze- szkodzie, aby inna metoda zwraca(cid:239)a inny typ danych, który b(cid:218)dzie najprzydatniejszy. Teraz w pakiecie config zdefiniuj klas(cid:218) PicturesUploadProperties: @SpringBootApplication @EnableConfigurationProperties({PictureUploadProperties.class}) public class MasterSpringMvc4Application extends WebMvcConfigurerAdapter { // kod pomini(cid:266)ty } W pliku application.properties zdefiniuj nast(cid:218)puj(cid:200)ce w(cid:239)a(cid:258)ciwo(cid:258)ci: upload.pictures.uploadPath=file:./pictures upload.pictures.anonymousPicture=classpath:/images/anonymous.png 105 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW Poniewa(cid:285) u(cid:285)yta zosta(cid:239)a klasa DefaultResourceLoader platformy Spring, mo(cid:285)na zastosowa(cid:202) prefiksy, np. file: lub classpath: do okre(cid:258)lenia po(cid:239)o(cid:285)enia zasobów. Jest to sposób alternatywny do utworzenia klasy FileSystemResource lub ClassPathResource. Zalet(cid:200) powy(cid:285)szej metody jest mo(cid:285)liwo(cid:258)(cid:202) dokumentowania kodu. Dzi(cid:218)ki niej od razu wida(cid:202), (cid:285)e katalog z obrazami znajduje si(cid:218) w g(cid:239)ównym katalogu obiektu, natomiast obraz przedstawiaj(cid:200)cy anonimowego u(cid:285)ytkownika znajduje si(cid:218) w pliku zapisanym w (cid:258)cie(cid:285)ce classpath. To wszystko. Teraz mo(cid:285)na wykorzysta(cid:202) zdefiniowane w(cid:239)a(cid:258)ciwo(cid:258)ci w kontrolerze. Poni(cid:285)ej przed- stawione s(cid:200) odpowiednie cz(cid:218)(cid:258)ci klasy PictureUploadController: package masterSpringMvc.profile; import masterSpringMvc.config.PictureUploadProperties; import org.apache.tomcat.util.http.fileupload.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLConnection; @Controller public class PictureUploadController { private final Resource picturesDir; private final Resource anonymousPicture; @Autowired public PictureUploadController(PictureUploadProperties uploadProperties) { picturesDir = uploadProperties.getUploadPath(); anonymousPicture = uploadProperties.getAnonymousPicture(); } @RequestMapping(value = /uploadedPicture ) public void getUploadedPicture(HttpServletResponse response) throws (cid:180)IOException { response.setHeader( Content-Type , URLConnection.guessContentTypeFromName(anonymousPicture.getFilename())); IOUtils.copy(anonymousPicture.getInputStream(), response.getOutputStream()); } private Resource copyFileToPictures(MultipartFile file) throws IOException { 106 Poleć książkęKup książkę Rozdzia(cid:225) 4. • (cid:224)adowanie plików i obs(cid:225)uga b(cid:225)(cid:266)dów String fileExtension = getFileExtension(file.getOriginalFilename()); File tempFile = File.createTempFile( pic , fileExtension, picturesDir.getFile()); try (InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream(tempFile)) { IOUtils.copy(in, out); } return new FileSystemResource(tempFile); } // Pozosta(cid:225)a cz(cid:266)(cid:286)(cid:252) kodu pozostaje bez zmian } Je(cid:285)eli uruchomisz aplikacj(cid:218) ponownie, stwierdzisz, (cid:285)e strona si(cid:218) nie zmieni(cid:239)a. Obraz anoni- mowego u(cid:285)ytkownika b(cid:218)dzie dalej pokazywany, a obrazy (cid:239)adowane przez u(cid:285)ytkowników b(cid:218)d(cid:200) dalej zapisywane w podkatalogu pictures w g(cid:239)ównym katalogu projektu. Wy(cid:258)wietlenie za(cid:239)adowanego obrazu Teraz dobrze by(cid:239)oby wy(cid:258)wietli(cid:202) obraz za(cid:239)adowany przez u(cid:285)ytkownika, prawda? W tym celu do klasy PictureUploadController dodaj atrybut modelu: @ModelAttribute( picturePath ) public Resource picturePath() { return anonymousPicture; } Umie(cid:258)(cid:202) powy(cid:285)szy atrybut w kodzie, aby odczyta(cid:202) jego warto(cid:258)(cid:202) po za(cid:239)adowaniu obrazu: @RequestMapping(value = /uploadedPicture ) public void getUploadedPicture(HttpServletResponse response, @ModelAttribute( picturePath ) Path picturePath) throws IOException { response.setHeader( Content-Type , URLConnection.guessContentTypeFromName(picturePath.toString())); Files.copy(picturePath, response.getOutputStream()); } Adnotacja @ModelAttribute umo(cid:285)liwia wygodne tworzenie atrybutów modelu dla wybranych metod, które potem mo(cid:285)na umieszcza(cid:202) w metodzie kontrolera z t(cid:200) sam(cid:200) adnotacj(cid:200). W powy(cid:285)szym kodzie parametr picturePath b(cid:218)dzie dost(cid:218)pny w modelu danych do czasu przekierowania u(cid:285)ytkownika na inn(cid:200) stron(cid:218). Jego domy(cid:258)ln(cid:200) warto(cid:258)ci(cid:200) jest zdefiniowana we w(cid:239)a(cid:258)ciwo(cid:258)ciach nazwa pliku z obrazem anonimowego u(cid:285)ytkownika. Po za(cid:239)adowaniu pliku trzeba zaktualizowa(cid:202) warto(cid:258)(cid:202) atrybutu. Zmie(cid:241) w tym celu metod(cid:218) onUpload: @RequestMapping(value = /upload , method = RequestMethod.POST) public String onUpload(MultipartFile file, RedirectAttributes redirectAttrs, Model model) throws IOException { if (file.isEmpty() || !isImage(file)) { 107 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW redirectAttrs.addFlashAttribute( error , Niew(cid:239)a(cid:258)ciwy plik. Za(cid:239)aduj plik z obrazem. ); return redirect:/upload ; } Resource picturePath = copyFileToPictures(file); model.addAttribute( picturePath , picturePath); return profile/uploadPage ; } Dzi(cid:218)ki umieszczeniu w kodzie odwo(cid:239)ania do modelu mo(cid:285)na odczyta(cid:202) warto(cid:258)(cid:202) parametru picture (cid:180)Path po za(cid:239)adowaniu obrazu. Teraz problem polega na tym, (cid:285)e metody onUpload i getUploadedPicture s(cid:200) wywo(cid:239)ywane w ró(cid:285)- nych zapytaniach. Niestety, warto(cid:258)ci atrybutów modelu s(cid:200) usuwane pomi(cid:218)dzy kolejnymi za- pytaniami. Dlatego parametr picturePath trzeba zdefiniowa(cid:202) jako atrybut sesji. W tym celu klas(cid:218) kon- trolera nale(cid:285)y opatrzy(cid:202) dodatkow(cid:200) adnotacj(cid:200): @Controller @SessionAttributes( picturePath ) public class PictureUploadController { } Uff! Sporo adnotacji jak na prost(cid:200) obs(cid:239)ug(cid:218) atrybutu sesji. Teraz strona b(cid:218)dzie wygl(cid:200)da(cid:239)a jak poni(cid:285)ej: W ten sposób mo(cid:285)na naprawd(cid:218) (cid:239)atwo tworzy(cid:202) kod. Ponadto nie trzeba bezpo(cid:258)rednio stosowa(cid:202) interfejsu HttpServletRequest ani HttpSession. Co wi(cid:218)cej, mo(cid:285)na równie(cid:285) (cid:239)atwo okre(cid:258)la(cid:202) typ obiektu. Obs(cid:239)uga b(cid:239)(cid:218)dów (cid:239)adowania plików Wnikliwy Czytelnik z pewno(cid:258)ci(cid:200) zauwa(cid:285)y, (cid:285)e powy(cid:285)szy kod mo(cid:285)e zg(cid:239)asza(cid:202) dwa rodzaje wy- j(cid:200)tków: 108 Poleć książkęKup książkę Rozdzia(cid:225) 4. • (cid:224)adowanie plików i obs(cid:225)uga b(cid:225)(cid:266)dów (cid:81) IOException: wyj(cid:200)tek zg(cid:239)aszany w sytuacji, gdy co(cid:258) z(cid:239)ego wydarzy si(cid:218) podczas zapisywania pliku na dysku; (cid:81) MultipartException: wyj(cid:200)tek zg(cid:239)aszany w sytuacji, gdy podczas (cid:239)adowania pliku wyst(cid:200)pi b(cid:239)(cid:200)d, na przyk(cid:239)ad przekroczona zostanie maksymalna wielko(cid:258)(cid:202) pliku. Teraz jest dobra okazja do zapoznania si(cid:218) z dwoma sposobami obs(cid:239)ugi b(cid:239)(cid:218)dów w (cid:258)rodowisku Spring: (cid:81) poprzez lokalne u(cid:285)ycie adnotacji @ExceptionHandler w metodzie kontrolera, (cid:81) poprzez zdefiniowanie globalnego kontenera serwletów. Obs(cid:239)u(cid:285) wyj(cid:200)tek IOException za pomoc(cid:200) poni(cid:285)szej metody z adnotacj(cid:200) @ExceptionHandler, utwo- rzonej w klasie PictureUploadController: @ExceptionHandler(IOException.class) public ModelAndView handleIOException(IOException exception) { ModelAndView modelAndView = new ModelAndView( profile/uploadPage ); modelAndView.addObject( error , exception.getMessage()); return modelAndView; } To prosty, ale skuteczny sposób. Powy(cid:285)sza metoda b(cid:218)dzie wywo(cid:239)ywana za ka(cid:285)dym razem, gdy w kontrolerze zostanie zg(cid:239)oszony wyj(cid:200)tek IOException. Zg(cid:239)aszanie wyj(cid:200)tków w kodzie Java jest do(cid:258)(cid:202) trudne, wi(cid:218)c w celu sprawdzenia dzia(cid:239)ania me- tody obs(cid:239)ugi wyj(cid:200)tku zmie(cid:241) na czas testów kod metody onUpload w nast(cid:218)puj(cid:200)cy sposób: @RequestMapping(value = /upload , method = RequestMethod.POST) public String onUpload(MultipartFile file, RedirectAttributes redirectAttrs, Model model) throws IOException { throw new IOException( Komunikat testowy ); } Po wprowadzeniu powy(cid:285)szych zmian i za(cid:239)adowaniu obrazu na stronie pojawi si(cid:218) poni(cid:285)szy komunikat: 109 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW Teraz trzeba obs(cid:239)u(cid:285)y(cid:202) wyj(cid:200)tek MultipartException. Nale(cid:285)y to zrobi(cid:202) na poziomie kontenera serwletów (czyli serwera Tomcat), poniewa(cid:285) wyj(cid:200)tek ten nie jest zg(cid:239)aszany bezpo(cid:258)rednio przez kontroler. W tym celu musisz utworzy(cid:202) now(cid:200) metod(cid:218) konfiguracyjn(cid:200) EmbeddedServletContainerCustomizer w klasie WebConfiguration: @Bean public EmbeddedServletContainerCustomizer containerCustomizer() { EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer = new EmbeddedServletContainerCustomizer() { @Override public void customize(ConfigurableEmbeddedServletContainer container) { container.addErrorPages(new ErrorPage(MultipartException.class, (cid:180) /uploadError )); } }; return embeddedServletContainerCustomizer; } Kod jest nieco rozwlek(cid:239)y. Zwró(cid:202) uwag(cid:218), (cid:285)e EmbeddedServletContainerCustomizer to interfejs zawieraj(cid:200)cy jedn(cid:200) metod(cid:218), któr(cid:200) mo(cid:285)na zast(cid:200)pi(cid:202) wyra(cid:285)eniem lambda: @Bean public EmbeddedServletContainerCustomizer containerCustomizer() { EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer = container - container.addErrorPages(new ErrorPage(MultipartException.class, /uploadError )); return embeddedServletContainerCustomizer; } Mo(cid:285)na wi(cid:218)c po prostu napisa(cid:202) taki kod: @Bean public EmbeddedServletContainerCustomizer containerCustomizer() { return container - container.addErrorPages(new ErrorPage(MultipartException.class, /uploadError )); } Powy(cid:285)szy kod tworzy now(cid:200) stron(cid:218) z komunikatem o b(cid:239)(cid:218)dzie, która b(cid:218)dzie wy(cid:258)wietlana w momen- cie zg(cid:239)oszenia wyj(cid:200)tku MultipartException. B(cid:218)dzie ona równie(cid:285) powi(cid:200)zana z kodem HTTP. Interfejs EmbeddedServletContainerCustomizer posiada wiele innych funkcjonalno(cid:258)ci umo(cid:285)li- wiaj(cid:200)cych dostosowanie kontenera serwletów odpowiednio do dzia(cid:239)ania aplikacji. Wi(cid:218)cej informa- cji na ten temat jest dost(cid:218)pnych pod adresem http://docs.spring.io/spring-boot/docs/current/ refe- rence/html/boot-features-developing-web-applications.html#boot-features-customizing-embedded- containers. 110 Poleć książkęKup książkę Rozdzia(cid:225) 4. • (cid:224)adowanie plików i obs(cid:225)uga b(cid:225)(cid:266)dów Teraz w klasie PictureUploadController trzeba obs(cid:239)u(cid:285)y(cid:202) adres URL uploadError: @RequestMapping( uploadError ) public ModelAndView onUploadError(HttpServletRequest request) { ModelAndView modelAndView = new ModelAndView( uploadPage ); modelAndView.addObject( error , request.getAttribute(WebUtils.ERROR_MESSAGE_ATTRIBUTE)); return modelAndView; } Strony z komunikatami o b(cid:239)(cid:218)dach zdefiniowane w kontenerze serwletów posiadaj(cid:200) kilka cie- kawych atrybutów umo(cid:285)liwiaj(cid:200)cych diagnozowanie b(cid:239)(cid:218)dów: Atrybut Opis javax.servlet.error.status_code javax.servlet.error.exception_type javax.servlet.error.message javax.servlet.error.request_uri javax.servlet.error.exception javax.servlet.error.servlet_name Kod HTTP b(cid:239)(cid:218)du Klasa wyj(cid:200)tku Komunikat o wyj(cid:200)tku Adres URL, dla którego zosta(cid:239) zg(cid:239)oszony wyj(cid:200)tek W(cid:239)a(cid:258)ciwy wyj(cid:200)tek Nazwa serwletu zg(cid:239)aszaj(cid:200)cego wyj(cid:200)tek Z powy(cid:285)szych atrybutów mo(cid:285)na wygodnie korzysta(cid:202) dzi(cid:218)ki klasie WebUtils z biblioteki Spring Web. Je(cid:285)eli u(cid:285)ytkownik spróbuje za(cid:239)adowa(cid:202) bardzo du(cid:285)y obraz, pojawi si(cid:218) czytelny komunikat. Teraz mo(cid:285)esz sprawdzi(cid:202), czy b(cid:239)(cid:200)d jest poprawnie obs(cid:239)ugiwany, (cid:239)aduj(cid:200)c du(cid:285)y plik (wi(cid:218)kszy ni(cid:285) 1 MB) lub ustawiaj(cid:200)c w(cid:239)a(cid:258)ciwo(cid:258)(cid:202) multipart.maxFileSize na mniejsz(cid:200) warto(cid:258)(cid:202), na przyk(cid:239)ad 1 kB. 111 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW T(cid:239)umaczenia komunikatów o b(cid:239)(cid:218)dach Dla programisty komunikaty o wyj(cid:200)tkach wy(cid:258)wietlane na stronie s(cid:200) naprawd(cid:218) cenne, jednak dla u(cid:285)ytkownika nie przedstawiaj(cid:200) wi(cid:218)kszej warto(cid:258)ci. Dlatego musisz je przet(cid:239)umaczy(cid:202). W tym celu w konstruktorze kontrolera nale(cid:285)y zastosowa(cid:202) klas(cid:218) MessageSource: private final MessageSource messageSource; @Autowired public PictureUploadController(PictureUploadProperties uploadProperties, MessageSource messageSource) { picturesDir = uploadProperties.getUploadPath(); anonymousPicture = uploadProperties.getAnonymousPicture(); this.messageSource = messageSource; } Teraz mo(cid:285)esz odczytywa(cid:202) komunikaty zapisane w pliku: @ExceptionHandler(IOException.class) public ModelAndView handleIOException(Locale locale) { ModelAndView modelAndView = new ModelAndView( profile/uploadPage ); modelAndView.addObject( error , messageSource.getMessage( upload.io.exception , null, locale)); return modelAndView; } @RequestMapping( uploadError ) public ModelAndView onUploadError(Locale locale) { ModelAndView modelAndView = new ModelAndView( profile/uploadPage ); modelAndView.addObject( error , messageSource.getMessage( upload.file.too.big , null, locale)); return modelAndView; } Poni(cid:285)ej zdefiniowane s(cid:200) komunikaty: upload.io.exception=Podczas (cid:239)adowania pliku wyst(cid:200)pi(cid:239) b(cid:239)(cid:200)d. Spróbuj jeszcze raz. upload.file.too.big=Plik jest za du(cid:285)y. I jeszcze komunikaty w j(cid:218)zyku angielskim: upload.io.exception=An error occurred while uploading the file. Please try again. upload.file.too.big=Your file is too big. Zapisywanie profilu u(cid:285)ytkownika w sesji Kolejn(cid:200) niezb(cid:218)dn(cid:200) rzecz(cid:200) jest zapisywanie profilu u(cid:285)ytkownika w sesji, dzi(cid:218)ki czemu informacje nie b(cid:218)d(cid:200) z niego usuwane przy ka(cid:285)dorazowym wy(cid:258)wietleniu strony. Takie dzia(cid:239)anie aplikacji by(cid:239)oby dla u(cid:285)ytkowników irytuj(cid:200)ce, wi(cid:218)c musisz to zmieni(cid:202). 112 Poleć książkęKup książkę Rozdzia(cid:225) 4. • (cid:224)adowanie plików i obs(cid:225)uga b(cid:225)(cid:266)dów Sesje HTTP umo(cid:285)liwiaj(cid:200) przechowywanie informacji przesy(cid:239)anych w zapytaniach. Protokó(cid:239) HTTP jest protoko(cid:239)em bezstanowym, tzn. nie ma mo(cid:285)liwo(cid:258)ci powi(cid:200)zania dwóch zapyta(cid:241) wysy(cid:239)anych przez tego samego u(cid:285)ytkownika. Wi(cid:218)kszo(cid:258)(cid:202) kontenerów serwletów przypisuje ka(cid:285)demu u(cid:285)ytkownikowi plik cia- steczka o nazwie JSESSIONID. Plik ten jest przesy(cid:239)any w nag(cid:239)ówku zapytania i umo(cid:285)liwia zapisywanie dowolnych obiektów w mapie o nazwie HttpSession (sesja HTTP). Taka sesja zazwyczaj ko(cid:241)czy si(cid:218) w chwili zamkni(cid:218)cia przegl(cid:200)darki przez u(cid:285)ytkownika lub po okre(cid:258)lonym czasie braku jego aktywno(cid:258)ci. Wcze(cid:258)niej pozna(cid:239)e(cid:258) sposób umieszczania obiektów w sesji za pomoc(cid:200) adnotacji @SessionAttri (cid:180)butes. Ta metoda dobrze sprawdza si(cid:218) wewn(cid:200)trz jednego kontrolera, ale gdy jest ich kilka, wtedy pojawiaj(cid:200) si(cid:218) problemy ze wspó(cid:239)dzieleniem danych. Aby móc identyfikowa(cid:202) atrybut na podstawie jego nazwy, trzeba stosowa(cid:202) ci(cid:200)g znaków, co utrudnia modyfikacj(cid:218) kodu. Z tego samego powodu nie mo(cid:285)na bezpo(cid:258)rednio manipulowa(cid:202) obiektem HttpSession. Kolejnym ar- gumentem przemawiaj(cid:200)cym przeciwko bezpo(cid:258)redniemu manipulowaniu sesj(cid:200) s(cid:200) utrudnienia w wykonywaniu testów jednostkowych kontrolera. W (cid:258)rodowisku Spring stosuje si(cid:218) jednak inny popularny sposób zapisywania informacji w sesji, mianowicie opatrywanie metody adnotacj(cid:200) @Scope( session ). Dzi(cid:218)ki temu mo(cid:285)na umieszcza(cid:202) metody w kontrolerach lub innych komponentach aplikacji Spring, aby odczytywa(cid:202) b(cid:200)d(cid:283) zapisywa(cid:202) w nich dane. Utwórz w pakiecie profile klas(cid:218) UserProfileSession: package masterSpringMvc.profile; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Component; import java.io.Serializable; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; @Component @Scope(value = session , proxyMode = ScopedProxyMode.TARGET_CLASS) public class UserProfileSession implements Serializable { private String twitterHandle; private String email; private LocalDate birthDate; private List String tastes = new ArrayList (); public void saveForm(ProfileForm profileForm) { this.twitterHandle = profileForm.getTwitterHandle(); this.email = profileForm.getEmail(); this.birthDate = profileForm.getBirthDate(); this.tastes = profileForm.getTastes(); } 113 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW public ProfileForm toForm() { ProfileForm profileForm = new ProfileForm(); profileForm.setTwitterHandle(twitterHandle); profileForm.setEmail(email); profileForm.setBirthDate(birthDate); profileForm.setTastes(tastes); return profileForm; } } Zaimplementowa(cid:239)e(cid:258) wygodny sposób odczytywania i zapisywania danych w postaci obiektu ProfileForm. Dzi(cid:218)ki temu b(cid:218)dziesz móg(cid:239) zapisywa(cid:202) i odczytywa(cid:202) dane z kontrolera Profile (cid:180)Controller. Musisz w nim umie(cid:258)ci(cid:202) zmienn(cid:200) typu UserProfileSession i zapisa(cid:202) j(cid:200) jako pole klasy. Ponadto musisz udost(cid:218)pni(cid:202) obiekt ProfileForm jako atrybut modelu, dzi(cid:218)ki czemu nie trzeba b(cid:218)dzie stosowa(cid:202) go w metodzie displayProfile. Na koniec dane profilu po ich spraw- dzeniu nale(cid:285)y zapisa(cid:202): @Controller public class ProfileController { private UserProfileSession userProfileSession; @Autowired public ProfileController(UserProfileSession userProfileSession) { this.userProfileSession = userProfileSession; } @ModelAttribute public ProfileForm getProfileForm() { return userProfileSession.toForm(); } @RequestMapping(value = /profile , params = { save }, method = (cid:180)RequestMethod.POST) public String saveProfile(@Valid ProfileForm profileForm, BindingResult bindingResult) { if (bindingResult.hasErrors()) { return profile/profilePage ; } userProfileSession.saveForm(profileForm); return redirect:/profile ; } // Pozosta(cid:225)a cz(cid:266)(cid:286)(cid:252) kodu bez zmian } To wszystko, co trzeba zrobi(cid:202) w aplikacji Spring, aby zapisywa(cid:202) dane w sesji. Je(cid:285)eli teraz u(cid:285)ytkownik wype(cid:239)ni formularz i od(cid:258)wie(cid:285)y stron(cid:218), wprowadzone dane b(cid:218)d(cid:200) zachowane. Teraz zamierzam opisa(cid:202) kilka u(cid:285)ytych wcze(cid:258)niej poj(cid:218)(cid:202). 114 Poleć książkęKup książkę Rozdzia(cid:225) 4. • (cid:224)adowanie plików i obs(cid:225)uga b(cid:225)(cid:266)dów Pierwsze z nich to umieszczanie (wstrzykiwanie) obiektów w konstruktorze. Konstruktor kontrolera ProfileController jest opatrzony adnotacj(cid:200) @Autowired, oznaczaj(cid:200)c(cid:200), (cid:285)e platforma Spring b(cid:218)dzie przetwarza(cid:239)a argumenty konstruktora przed utworzeniem instancji klasy. Alterna- tywnym rozwi(cid:200)zaniem, nieco mniej obszernym, jest zastosowanie wstrzykiwania pól: @Controller public class ProfileController { @Autowired private UserProfileSession userProfileSession; } Wstrzykiwanie obiektów w konstruktorze jest zdecydowanie lepszym sposobem, poniewa(cid:285) u(cid:239)atwia wykonywanie testów jednostkowych, gdyby trzeba by(cid:239)o zrezygnowa(cid:202) z testów oferowa- nych przez platform(cid:218) Spring. Ponadto zale(cid:285)no(cid:258)ci mi(cid:218)dzy klasami s(cid:200) nieco (cid:258)ci(cid:258)lejsze. Szczegó(cid:239)owy opis wstrzykiwania pól i konstruktorów jest zawarty w doskona(cid:239)ym wpisie na blogu Olivera Gierkego pod adresem http://olivergierke.de/2013/11/why-field-injection-is-evil. Kolejn(cid:200) rzecz(cid:200) wymagaj(cid:200)c(cid:200) wyja(cid:258)nienia jest parametr proxyMode (tryb serwera proxy) w adnota- cji @Scope: @Scope(value = session , proxyMode = ScopedProxyMode.TARGET_CLASS) W platformie Spring dost(cid:218)pne s(cid:200) trzy warto(cid:258)ci parametru proxyMode, nie licz(cid:200)c domy(cid:258)lnego: (cid:81) TARGET_CLASS — oznaczaj(cid:200)cy zastosowanie serwera CGI, (cid:81) INTERFACES — oznaczaj(cid:200)cy zastosowanie serwera JDK, (cid:81) NO — oznaczaj(cid:200)cy brak serwera proxy. Zaleta serwera proxy zazwyczaj ujawnia si(cid:218) podczas wstrzykiwania obiektów do d(cid:239)ugotrwa- (cid:239)ych komponentów, na przyk(cid:239)ad singletonów. Poniewa(cid:285) wstrzykiwanie odbywa si(cid:218) tylko raz, podczas tworzenia klasy Bean, wi(cid:218)c w kolejnych wywo(cid:239)aniach tej klasy nie jest uwzgl(cid:218)dniany jej bie(cid:285)(cid:200)cy stan. W Twoim przypadku aktualny stan klasy Bean jest zapisywany w sesji, a nie bezpo(cid:258)rednio w samej klasie. Z tego powodu platforma Spring musi utworzy(cid:202) serwer proxy, który przechwy- tuje wywo(cid:239)ania metod klasy i wykrywa ich mutacje. Dzi(cid:218)ki temu stan klasy mo(cid:285)e by(cid:202) zapisy- wany i odczytywany z sesji HTTP. W przypadku klasy sesji trzeba stosowa(cid:202) tryb proxy. Serwer CGI dokonuje instrumentalizacji kodu bajtowego i wspó(cid:239)pracuje z ka(cid:285)d(cid:200) klas(cid:200), natomiast serwer JDK jest nieco mniej inge- rencyjny, ale wymaga zaimplementowania interfejsu. Ponadto klasa UserProfileSession zosta(cid:239)a zdefiniowana jako implementacja interfejsu Seria (cid:180)lizable. Nie jest to konieczne, poniewa(cid:285) sesja HTTP mo(cid:285)e przechowywa(cid:202) w pami(cid:218)ci do- wolne obiekty, ale dobr(cid:200) praktyk(cid:200) jest tworzenie obiektów, które mo(cid:285)na serializowa(cid:202). 115 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW W rzeczywisto(cid:258)ci mo(cid:285)na zmieni(cid:202) sposób przechowywania danych w sesji. W rozdziale 8., „Optymalizacja zapyta(cid:241)”, poznasz sposób zapisywania ich w bazie danych Redis, która wspó(cid:239)- pracuje z obiektami typu Serializable. Najlepiej jest traktowa(cid:202) sesj(cid:218) jako uniwersalny maga- zyn danych. Zadaniem programisty jest opracowanie sposobu zapisywania i odczytywania da- nych z tego magazynu. Aby serializacja klasy przebiega(cid:239)a poprawnie, ka(cid:285)de jej pole równie(cid:285) musi umo(cid:285)liwia(cid:202) seriali- zacj(cid:218). W naszym przypadku ci(cid:200)gi znaków oraz daty spe(cid:239)niaj(cid:200) ten warunek, wi(cid:218)c nic nie stoi na przeszkodzie. W(cid:239)asne strony z komunikatami o b(cid:239)(cid:218)dach Platforma Spring Boot umo(cid:285)liwia definiowanie w(cid:239)asnych widoków z komunikatami o b(cid:239)(cid:218)dach, wy(cid:258)wietlanych zamiast opisanej wcze(cid:258)niej strony z komunikatem Whitelabel Error Page. Widok musi mie(cid:202) nazw(cid:218) error (b(cid:239)(cid:200)d). Jego przeznaczeniem jest obs(cid:239)uga wszystkich wyj(cid:200)tków. Domy(cid:258)lna klasa BasicErrorController udost(cid:218)pnia wiele przydatnych atrybutów modelu danych, które mo(cid:285)na wykorzysta(cid:202) na takiej stronie. Utwórz w katalogu src/main/resources/templates w(cid:239)asn(cid:200) stron(cid:218) error.html do wy(cid:258)wietlania komu- nikatów o b(cid:239)(cid:218)dach: !DOCTYPE html html xmlns:th= http://www.thymeleaf.org head lang= en meta charset= UTF-8 / title th:text= ${status} 404 /title link href= materialize/css/materialize.css type= text/css rel= stylesheet media= screen,projection / /head body div class= row h1 class= indigo-text center th:text= ${error} Strona nie istnieje /h1 p class= col s12 center th:text= ${message} (cid:191)(cid:200)dana strona nie istnieje /p /div /body /html Je(cid:285)eli teraz u(cid:285)ytkownik otworzy adres URL, który nie b(cid:218)dzie obs(cid:239)ugiwany przez aplikacj(cid:218), pojawi si(cid:218) w(cid:239)asna strona z komunikatem: 116 Poleć książkęKup książkę Rozdzia(cid:225) 4. • (cid:224)adowanie plików i obs(cid:225)uga b(cid:225)(cid:266)dów Bardziej zaawansowanym sposobem obs(cid:239)ugi b(cid:239)(cid:218)dów jest zaimplementowanie klasy ErrorCon (cid:180)troller, czyli kontrolera do obs(cid:239)ugi wyj(cid:200)tków na poziomie globalnym. Przyjrzyj si(cid:218) klasom ErrorMvcAutoConfiguration i BasicErrorController, b(cid:218)d(cid:200)cym standardowymi implementa- cjami tego kontrolera. Zmienne tablicowe w adresach URL Teraz wiesz ju(cid:285), jakimi tematami jest zainteresowany u(cid:285)ytkownik. Warto by(cid:239)oby ulepszy(cid:202) kontroler tak, aby wyszukiwa(cid:239) tweety zawieraj(cid:200)ce s(cid:239)owa kluczowe z zadanej listy. Jednym z ciekawych sposobów przekazywania w adresie URL par danych klucz-warto(cid:258)(cid:202) jest zastosowanie zmiennych tablicowych. Mo(cid:285)na to (cid:239)atwo osi(cid:200)gn(cid:200)(cid:202) za pomoc(cid:200) parametrów zapy- ta(cid:241). Przyjrzyj si(cid:218) poni(cid:285)szemu kodowi: jaki(cid:258)Url/parametr?zmienna1=warto(cid:258)(cid:202)1 zmienna2=warto(cid:258)(cid:202)2 Zamiast parametru mo(cid:285)na zastosowa(cid:202) tablic(cid:218) zmiennych, jak poni(cid:285)ej: jaki(cid:258)Url/parametr;zmienna1=warto(cid:258)(cid:202)1;zmienna2=warto(cid:258)(cid:202)2 Ka(cid:285)da warto(cid:258)(cid:202) mo(cid:285)e by(cid:202) równie(cid:285) list(cid:200): jaki(cid:258)Url/parametr;zmienna1=warto(cid:258)(cid:202)1,warto(cid:258)(cid:202)2;zmienna2=warto(cid:258)(cid:202)3,warto(cid:258)(cid:202)4 Zmienna tablicowa mo(cid:285)e zosta(cid:202) skojarzona w kontrolerze z obiektami ró(cid:285)nych typów, na przyk(cid:239)ad: (cid:81) Map String, List ? — typ obs(cid:239)uguj(cid:200)cy wiele zmiennych z wieloma warto(cid:258)ciami; (cid:81) Map String, ? — typ obs(cid:239)uguj(cid:200)cy zmienne z pojedynczymi warto(cid:258)ciami; (cid:81) List ? — typ stosowany w przypadku, gdy potrzebna jest jedna zmienna, której nazw(cid:218) mo(cid:285)na konfigurowa(cid:202). W Twoim przypadku wymagana jest obs(cid:239)uga adresu jak poni(cid:285)ej: http://localhost:8080/search/popular;keywords=spring,java 117 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW Pierwszy parametr, popular, oznacza typ wyniku stosowany w interfejsie API do przeszuki- wania serwisu Twitter. Parametr ten mo(cid:285)e przyjmowa(cid:202) warto(cid:258)ci mixed (mieszane), recent (najnow- sze) lub popular (popularne). Pozosta(cid:239)a cz(cid:218)(cid:258)(cid:202) adresu URL zawiera list(cid:218) s(cid:239)ów kluczowych, któr(cid:200) mo(cid:285)na skojarzy(cid:202) z obiektem typu List String . Domy(cid:258)lnie platforma Spring MVC usuwa z adresu URL wszystkie znaki za (cid:258)rednikiem. Za- tem pierwsz(cid:200) rzecz(cid:200), jak(cid:200) trzeba zrobi(cid:202), aby umo(cid:285)liwi(cid:202) zastosowanie zmiennych tablicowych, jest wy(cid:239)(cid:200)czenie tej funkcjonalno(cid:258)ci. Wpisz w definicji klasy WebConfiguration poni(cid:285)szy kod: @Override public void configurePathMatch(PathMatchConfigurer configurer) { UrlPathHelper urlPathHelper = new UrlPathHelper(); urlPathHelper.setRemoveSemicolonContent(false); configurer.setUrlPathHelper(urlPathHelper); } Utwórz w pakiecie search nowy kontroler o nazwie SearchController. Jego zadaniem b(cid:218)dzie obs(cid:239)uga nast(cid:218)puj(cid:200)cego zapytania: package masterSpringMvc.search; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.social.twitter.api.Tweet; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.MatrixVariable; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import java.util.List; @Controller public class SearchController { private SearchService searchService; @Autowired public SearchController(SearchService searchService) { this.searchService = searchService; } @RequestMapping( /search/{searchType} ) public ModelAndView search(@PathVariable String searchType, @MatrixVariable List String keywords) { List Tweet tweets = searchService.search(searchType, keywords); ModelAndView modelAndView = new ModelAndView( resultPage ); modelAndView.addObject( tweets , tweets); 118 Poleć książkęKup książkę Rozdzia(cid:225) 4. • (cid:224)adowanie plików i obs(cid:225)uga b(cid:225)(cid:266)dów modelAndView.addObject( search , String.join( , , keywords)); return modelAndView; } } Jak wida(cid:202), do wy(cid:258)wietlenia tweetów mo(cid:285)na wykorzysta(cid:202) istniej(cid:200)c(cid:200) stron(cid:218) z wynikami wyszu- kiwania. Ponadto wyszukiwanie b(cid:218)dzie realizowane przez inn(cid:200) klas(cid:218), o nazwie SearchService (us(cid:239)uga wyszukiwania). Us(cid:239)ug(cid:218) t(cid:218) zdefiniuj w tym samym pakiecie, w którym znajduje si(cid:218) kon- troler SearchController: package masterSpringMvc.search; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.social.twitter.api.Tweet; import org.springframework.social.twitter.api.Twitter; import org.springframework.stereotype.Service; import java.util.List; @Service public class SearchService { private Twitter twitter; @Autowired public SearchService(Twitter twitter) { this.twitter = twitter; } public List Tweet search(String searchType, List String keywords) { return null; } } Teraz musisz zaimplementowa(cid:202) metod(cid:218) search. Argumentem metody twitter.searchOpera (cid:180)tions().search(parametry) jest obiekt typu searchParameters, umo(cid:285)liwiaj(cid:200)cy zaawansowa- ne wyszukiwanie tweetów wed(cid:239)ug kilkunastu kryteriów. Ciebie interesuj(cid:200) atrybuty query (za- pytanie), resultType (typ wyniku) i count (liczba). Najpierw utwórz konstruktor typu ResultType z parametrem searchType. ResultType jest ty- pem wyliczeniowym, zatem mo(cid:285)na przegl(cid:200)da(cid:202) ró(cid:285)ne elementy obiektu i wyszukiwa(cid:202) te, które spe(cid:239)niaj(cid:200) zadane warunki, nie uwzgl(cid:218)dniaj(cid:200)c wielko(cid:258)ci znaków: private SearchParameters.ResultType getResultType(String searchType) { for (SearchParameters.ResultType knownType : SearchParameters.ResultType.values()) { if (knownType.name().equalsIgnoreCase(searchType)) { return knownType; } } return SearchParameters.ResultType.RECENT; } 119 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW Teraz utwórz nast(cid:218)puj(cid:200)c(cid:200) metod(cid:218) typu SearchParameters: private SearchParameters createSearchParam(String searchType, String taste) { SearchParameters.ResultType resultType = getResultType(searchType); SearchParameters searchParameters = new SearchParameters(taste); searchParameters.resultType(resultType); searchParameters.count(3); return searchParameters; } Teraz utworzenie konstruktora typu lista obiektów SearchParameters jest proste i polega jedy- nie na wykonaniu operacji mapowania (pobrania listy s(cid:239)ów kluczowych i zwrócenia dla ka(cid:285)- dego z nich konstruktora typu SearchParameters): List SearchParameters searches = keywords.stream() .map(taste - createSearchParam(searchType, taste)) .collect(Collectors.toList()); Teraz trzeba odczyta(cid:202) tweety z ka(cid:285)dego konstruktora SearchParameters. Mo(cid:285)na to zrobi(cid:202) w nast(cid:218)- puj(cid:200)cy sposób: List Tweet tweets = searches.stream() .map(params - twitter.searchOperations().search(params)) .map(searchResults - searchResults.getTweets()) .collect(Collectors.toList()); Je(cid:285)eli jednak si(cid:218) zastanowisz, odkryjesz, (cid:285)e powy(cid:285)szy kod zwraca list(cid:218) tweetów. Tobie jednak potrzebna jest prosta, „p(cid:239)aska” lista. Operacj(cid:218) przetwarzania mapy i „sp(cid:239)aszczenia” wyniku reali- zuje metoda flatMap. Mo(cid:285)na wi(cid:218)c utworzy(cid:202) nast(cid:218)puj(cid:200)cy kod: List Tweet tweets = searches.stream() .map(params - twitter.searchOperations().search(params)) .flatMap(searchResults - searchResults.getTweets().stream()) .collect(Collectors.toList()); Parametrem metody flatMap jest strumie(cid:241), co na pocz(cid:200)tku mo(cid:285)e wydawa(cid:202) si(cid:218) niezrozumia(cid:239)e. Przedstawi(cid:218) najpierw pe(cid:239)ny kod klasy SearchService, aby uzyska(cid:202) ca(cid:239)o(cid:258)ciowy obraz: package masterSpringMvc.search; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.social.twitter.api.SearchParameters; import org.springframework.social.twitter.api.Tweet; import org.springframework.social.twitter.api.Twitter; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; @Service public class SearchService { 120 Poleć książkęKup książkę Rozdzia(cid:225) 4. • (cid:224)adowanie plików i obs(cid:225)uga b(cid:225)(cid:266)dów private Twitter twitter; @Autowired public SearchService(Twitter twitter) { this.twitter = twitter; } public List Tweet search(String searchType, List String keywords) { List SearchParameters searches = keywords.stream() .map(taste - createSearchParam(searchType, taste)) .collect(Collectors.toList()); List Tweet results = searches.stream() .map(params - twitter.searchOperations() .search(params)) .flatMap(searchResults - searchResults.getTweets() .stream()) .collect(Collectors.toList()); return results; } private SearchParameters.ResultType getResultType(String searchType) { for (SearchParameters.ResultType knownType : SearchParameters.ResultType.values()) { if (knownType.name().equalsIgnoreCase(searchType)) { return knownType; } } return SearchParameters.ResultType.RECENT; } private SearchParameters createSearchParam(String searchType, String taste) { SearchParameters.ResultType resultType = getResultType(searchType); SearchParameters searchParameters = new SearchParameters(taste); searchParameters.resultType(resultType); searchParameters.count(3); return searchParameters; } } Teraz, po otwarciu adresu http://localhost:8080/search/mixed;keywords=spring,java, pojawi si(cid:218) spodziewany efekt, tj. najpierw wyniki wyszukania s(cid:239)owa Spring, a nast(cid:218)pnie Java (patrz rysunek na nast(cid:218)pnej stronie). Wszystko razem Do tej pory poszczególne funkcjonalno(cid:258)ci dzia(cid:239)a(cid:239)y osobno, wi(cid:218)c teraz pora je zebra(cid:202) wszyst- kie razem. Wykonaj nast(cid:218)puj(cid:200)ce kroki: 121 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW 1. Do strony profilu przenie(cid:258) formularz ze strony do (cid:239)adowania obrazów, a nast(cid:218)pnie usu(cid:241) j(cid:200). 2. Zmie(cid:241) przycisk Wy(cid:258)lij na stronie profilu tak, aby od razu rozpoczyna(cid:239) wyszukiwanie tweetów wed(cid:239)ug preferencji u(cid:285)ytkownika. 3. Zmie(cid:241) stron(cid:218) startow(cid:200) aplikacji tak, aby od razu pojawia(cid:239)y si(cid:218) na niej wyniki wyszukiwania spe(cid:239)niaj(cid:200)ce preferencje u(cid:285)ytkownika. Je(cid:285)eli preferencje nie b(cid:218)d(cid:200) zdefiniowane, powinna pojawi(cid:202) si(cid:218) strona profilu. Zach(cid:218)cam Ci(cid:218) do samodzielnego przygotowania kodu. By(cid:202) mo(cid:285)e po drodze napotkasz bardzo powa(cid:285)ne problemy, ale posiadasz ju(cid:285) wystarczaj(cid:200)c(cid:200) wiedz(cid:218), aby je samodzielnie rozwi(cid:200)za(cid:202). Wierz(cid:218) w Ciebie. OK. Je(cid:285)eli wykona(cid:239)e(cid:258) zadanie (na pewno tak), rzu(cid:202) okiem na moje rozwi(cid:200)zanie. Pierwszym krokiem by(cid:239)o usuni(cid:218)cie starej strony uploadPage.html. (cid:165)mia(cid:239)o, zrób to. Nast(cid:218)pnie tu(cid:285) po elemencie tytu(cid:239)u na stronie profilePage.html wpisa(cid:239)em poni(cid:285)sze wiersze: div class= row div class= col m8 s12 offset-m2 img th:src= @{/uploadedPicture} width= 100 height= 100 / /div div class= col s12 center red-text th:text= ${error} th:if= ${error} B(cid:239)(cid:200)d (cid:239)adowania pliku /div form th:action= @{/profile} method= post enctype= multipart/form-data class= col m8 s12 offset-m2 div class= input-field col s6 input type= file id= file name= file / /div div class= col s6 center 122 Poleć książkęKup książkę Rozdzia(cid:225) 4. • (cid:224)adowanie plików i obs(cid:225)uga b(cid:225)(cid:266)dów button class= btn indigo waves-effect waves-light type= submit (cid:180)name= upload th:text= #{upload} Za(cid:239)aduj i class= mdi-content-send right /i /button /div /form /div Powy(cid:285)szy kod jest bardzo podobny do kodu poprzedniej strony uploadPage.html. Usun(cid:200)(cid:239)em tylko tytu(cid:239) i zmieni(cid:239)em etykiet(cid:218) na przycisku do wysy(cid:239)ania formularza. W plikach ustawie(cid:241) regionalnych umie(cid:258)ci(cid:239)em te(cid:285) odpowiednie t(cid:239)umaczenia napisów: Dla j(cid:218)zyka polskiego: upload=Za(cid:239)aduj Dla j(cid:218)zyka angielskiego: upload=Upload Zmieni(cid:239)em równie(cid:285) nazw(cid:218) przycisku na upload. Dzi(cid:218)ki temu (cid:239)atwiej b(cid:218)dzie mo(cid:285)na zidentyfi- kowa(cid:202) operacj(cid:218) (cid:239)adowania pliku po stronie kontrolera. Teraz przy próbie za(cid:239)adowania obrazu u(cid:285)ytkownik zostanie przekierowany na star(cid:200) stron(cid:218) do (cid:239)adowania plików. Musia(cid:239)em poprawi(cid:202) kod metody onUpload w klasie PictureUploadController: @RequestMapping(value = /profile , params = { upload }, method = (cid:180)RequestMethod.POST) public String onUpload(@RequestParam MultipartFile file, RedirectAttributes redirectAttrs) throws IOException { if (file.isEmpty() || !isImage(file)) { redirectAttrs.addFlashAttribute( error , Niew(cid:239)a(cid:258)ciwy plik. Za(cid:239)aduj plik z obrazem. ); return redirect:/profile ; } Resource picturePath = copyFileToPictures(file); userProfileSession.setPicturePath(picturePath); return redirect:profile ; } Zwró(cid:202) uwag(cid:218), (cid:285)e adres URL obs(cid:239)uguj(cid:200)cy zapytanie POST zosta(cid:239) zmieniony z /upload na /profile. Obs(cid:239)uga formularza jest o wiele prostsza, je(cid:285)eli zapytania GET i POST dotycz(cid:200) tego samego ad- resu URL. W szczególno(cid:258)ci oszcz(cid:218)dzaj(cid:200) mnóstwa k(cid:239)opotów przy obs(cid:239)udze wyj(cid:200)tków, ponie- wa(cid:285) po wyst(cid:200)pieniu b(cid:239)(cid:218)du nie trzeba u(cid:285)ytkownika przekierowywa(cid:202) na inn(cid:200) stron(cid:218). Usun(cid:200)(cid:239)em równie(cid:285) atrybut picturePath modelu danych. Poniewa(cid:285) jest teraz klasa UserProfile (cid:180)Session reprezentuj(cid:200)ca sesj(cid:218) u(cid:285)ytkownika, wi(cid:218)c zosta(cid:239)a tutaj wykorzystana. Atrybut picturePath doda(cid:239)em do klasy UserProfileSession wraz z odpowiednimi metodami przypisuj(cid:200)cymi i od- czytuj(cid:200)cymi warto(cid:258)ci w(cid:239)a(cid:258)ciwo(cid:258)ci. 123 Poleć książkęKup książkę Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW Nie mog(cid:239)em zapomnie(cid:202) o zastosowaniu klasy UserProfileSession i udost(cid:218)pnieniu jej jako pola klasy PictureUploadController. Pami(cid:218)taj, (cid
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Spring MVC 4. Projektowanie zaawansowanych aplikacji WWW
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: