1 / 49

Веб-разработка на Java

Высшая школа ИТИС. Лекция 7 - Spring Web MVC Часть 2 23 октября 2013. Веб-разработка на Java. Алина Витальевна Васильева доцент факультета Компьютерных Наук Латвийский Университет инженер-разработчик, Одноклассники, Mail.ru Group alina.vasiljeva@gmail.com. Компоненты Spring MVC.

sewardj
Download Presentation

Веб-разработка на Java

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Высшая школа ИТИС Лекция 7 - Spring Web MVC Часть 2 23 октября 2013 Веб-разработка на Java Алина Витальевна Васильева доцент факультета Компьютерных Наук Латвийский Университет инженер-разработчик, Одноклассники, Mail.ru Group alina.vasiljeva@gmail.com

  2. Компоненты Spring MVC Spring MVC состоит из нескольких компонентов, между которыми происходит взаимодействие в процессе обработки клиентского запроса Рассмотрим подробнее контроллеры

  3. Demo Example View (single page) • a table displaying all existing users • a form for adding new users to a system Service Layer • storing data in-memory MVC Controller • the main topic, will experiment with it public interface UserService { public List<User> getUsers(); public void addUser(User user); }

  4. User Entity public class User { private long id; private String firstName; private String lastName; public long getId() { return id; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } /* ... setters ... */ }

  5. View: users.jsp (part-1: display) <c:choose> <c:when test="${not empty users}" > <h3>Users</h3> <table border="1"> <c:forEach var="user" items="${users}"> <tr> <td><c:out value="${user.id}"/></td> <td><c:out value="${user.firstName}"/></td> <td><c:out value="${user.lastName}"/></td> </tr> </c:forEach> </table> </c:when> <c:otherwise> <h3>No users in a system</h3> </c:otherwise> </c:choose>

  6. View: users.jsp (part-2: create) <h3>Add a new user</h3> <form action="/addUser" method="POST"> First name: <input id="firstName" name="firstName"/><br/> Last name: <input id="lastName" name="lastName"/><br/> <input type="submit" value="Add"/> </form> Something like that 

  7. Service Layer: UserServiceImpl @Component public class UserServiceImpl implements UserService { private AtomicLong idGenerator = new AtomicLong(0L); private static final List<User> users = new ArrayList<User>(); public List<User> getUsers() { return Collections.unmodifiableList(users); } public void addUser(User user) { user.setId(idGenerator.getAndIncrement()); users.add(user); } }

  8. Application Deployment <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>8.1.13.v20130916</version> <configuration> <scanIntervalSeconds>10</scanIntervalSeconds> <webApp> <contextPath>/my-app</contextPath> </webApp> </configuration> </plugin> <servlet> <servlet-name>spring</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/spring/*</url-pattern> </servlet-mapping> Jetty server configured in pom.xml to context path: /my-app Spring’s DispatcherServlet configured in web.xml to URL pattern: /spring/*

  9. Application URLs • As a result, DispatcherServlet will handle HTTP requests to the following base URL: http://localhost:8080/my-app/spring • Task 1: create a controller, map it to /users http://localhost:8080/my-app/spring/users and forward a request for rendering to users.jsp

  10. Making /users a main app page • Create a file \spring-app\src\main\webapp\index.jsp <% response.sendRedirect("/my-app/spring/users"); %> • As a result, requests to http://localhost:8080/my-app will be redirected to http://localhost:8080/my-app/spring/users

  11. Current Status http://localhost:8080/my-app/spring/users

  12. Implementing Controllers • Controllers interpret user input and transform it into a model that is rendered by the view • Spring introduces an annotation-based programming model for MVC controllers, using annotations such as • @Controller • @RequestMapping • @RequestParam • @ModelAttribute • etc

  13. Simplest Controller @Controller public class HelloWorldController { @RequestMapping("/helloWorld") public String helloWorld(Model model) { model.addAttribute("message", "Hello World!"); return "helloWorld"; } } • In this case, the method accepts a Model and returns a view name as a String • However, various other method parameters and return values can be used

  14. EnablingAnnotation Support To enable autodetection of annotated controllers, add component scanning to your configuration: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- Enable autodetection of annotated components --> <context:component-scan base-package="ru.kfu.itis" /> <!-- ... --> </beans>

  15. Annotation-based controllers • @Controllerannotation acts as a stereotype for the annotated class, indicating its role • The dispatcher scans such annotated classes for mapped methods and detects @RequestMapping annotations • @RequestMapping annotation maps URLs such as /helloWorldonto a particular controller method

  16. Mapping Requests • @RequestMapping annotation can map a request onto an entire class or a particular handler method • Typically the class-level annotation (not required) maps a specific request path onto a form controller • Additional method-level annotations narrow the primary mapping for a specific HTTP method request method ("GET", "POST", etc.)

  17. First Attempt @Controller @RequestMapping("/users") public class UsersController { @Autowired private UserService userService; public ModelAndView displayUsers() { ModelAndView mav = new ModelAndView("users"); List<User> users = userService.getUsers(); mav.addObject("users", users); return mav; } }

  18. Result [INFO] Configuring Jetty for project: spring-app ... [INFO] Context path = /my-app ... 2013-10-23 11:37:26.435:INFO:/my-app:Initializing Spring FrameworkServlet 'spring' ... INFO: Loading XML bean definitions from ServletContext resource [/WEB-INF/spring-servlet.xml] ... INFO: FrameworkServlet 'spring': initialization completed in 395 ms ... Okt 23, 2013 11:57:11 AM org.springframework.web.servlet.DispatcherServlet noHandlerFound WARNING: No mapping found for HTTP request with URI [/my-app/spring/users] in DispatcherServlet with name 'spring' Jetty console output And still ... 

  19. Second Attempt @Controller @RequestMapping("/users") public class UsersController { @Autowired private UserService userService; @RequestMapping public ModelAndView displayUsers() { ModelAndView mav = new ModelAndView("users"); List<User> users = userService.getUsers(); mav.addObject("users", users); return mav; } }

  20. Result Okt 23, 2013 11:58:59 AM org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler INFO: Mapped URL path [/users] onto handler 'usersController‘ Okt 23, 2013 11:58:59 AM org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler INFO: Mapped URL path [/users.*] onto handler 'usersController‘ Okt 23, 2013 11:58:59 AM org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler INFO: Mapped URL path [/users/] onto handler 'usersController' Jetty console output Success !!! 

  21. Conclusions • Controller annotated with @RequestMapping("/url") on a class-level only DOES NOT work • Adding @RequestMappingalso on a method-level WORKED successfully for HTTP GET request

  22. Interesting Fact • Controller method configured in such a way handles also HTTP POST requests • You can test it by adding debug print in a method <form action="/my-app/spring/users" method="POST"> First name: <input id="firstName" name="firstName"/><br/> Last name: <input id="lastName" name="lastName"/><br/> <input type="submit" value="Add"/> </form> @RequestMapping public ModelAndView displayUsers() { System.out.println("Inside displayUsers()"); ... }

  23. Separation of GET and POST @Controller @RequestMapping("/users") public class UsersController { @RequestMapping(method = RequestMethod.GET) public ModelAndView displayUsers() { ModelAndView mav = ... // business logic return mav; } @RequestMapping(method = RequestMethod.POST) public ModelAndView addUser() { ModelAndView mav = ... // business logic return mav; } } Obviously, we need two separate handler methods for GET (displaying a page) and POST (adding a new user)

  24. Implementing POST • We need to extract form parameters from HTTP request in some way • It can be achieved by adding HttpServletRequestto method signature @RequestMapping(method = RequestMethod.POST) public ModelAndView addUser(HttpServletRequest request) { User user = new User(); user.setFirstName( request.getParameter("firstName")); user.setLastName( request.getParameter("lastName")); userService.addUser(user); return new ModelAndView("users"); }

  25. Result • It works, but there are few issues: • Users table does not appear on the screen after form submit • Page Refresh (F5) leads to repeated POST request (the same user is created again) How to fix it ?

  26. Redirect After Submit pattern • There is a common problem with the handling the display of the confirmation page • The success view is rendered in the same request as the initial POST, leaving the browser in a state with the ability to replay the form submit • The user can simply reload the page, resubmitting the form

  27. Redirect After Submit pattern • Redirect After Submit pattern simply redirects the user to the success view instead of internally forwarding the request • A client redirect is not the same as a RequestDispatcher.forward() • Forwarding method internally redirect a request to another handler inside the servlet container • A client redirect instructs the client to issue another GET request

  28. Spring’s Support for Redirect • Special class exists for this purpose org.springframework.web.servlet.view.RedirectView • Spring MVC provides a shorthand for redirect views • Or even shorter: @RequestMapping(method = RequestMethod.POST) public ModelAndView addUser(HttpServletRequest request) { // create a user return new ModelAndView("redirect:users"); } @RequestMapping(method = RequestMethod.POST) public String addUser(HttpServletRequest request) { // create a user return "redirect:users"; }

  29. Success! • Users web-page is successfully implemented

  30. Домашнее задание 3 Реализовать веб-приложение «Калькулятор», используя Spring MVC фреймворк. Фактически цель задания – прочувствовать разницу с подходом программирования с использованием Java Servlet (1 домашняя работа). Базовая страница должна содержать форму, которая включает в себя: • два поля, куда пользователь вводит два числа • четыре кнопки арифметических действий: + - * / Результат выполнения арифметической операции должен быть отображён на том же экране. Решение до 25 октября 17:00 = 5 пунктов Решение до 27 октября 17:00 = 4 пункта

  31. Домашнее задание 4 • «Неофициальное» (без пунктов), но необходимое для выполнения следующего задания • Вычекать, настроить и запустить проект «News Feed» • Инструкции по настройке проекта: • Детали и проблемы обсуждаем в Google Group gitclone https://github.com/avasiljeva/lab02-newsfeed.git \lab02-newsfeed\README.md

  32. Spring MVC :: Continued

  33. More about controller methods • @RequestMappinghandler method can have a very flexible signatures • Arbitrary method name • Different types of arguments • Different options for return values • Most types of method arguments can be used in arbitrary order • BindingResult is the only exception

  34. Method arguments and return types • Servlet API: Request andResponse objects (ServletRequestor HttpServletRequest) • Servlet API: Session object (HttpSession) • @RequestParam annotated parameters for access to specific Servlet request parameters • @PathVariableannotated parameters for access to URI template variables • Many more: WebRequest,NativeWebRequest, Locale, Principal, @RequestHeader, @RequestBody, @InitBinder . . .

  35. @RequestParam @RequestMapping(method = RequestMethod.POST) public String addUser( @RequestParam String firstName, @RequestParam String lastName){ User user = new User(); user.setFirstName(firstName); user.setLastName(lastName); userService.addUser(user); return "redirect:users"; }

  36. Data Binding: @ModelAttribute • @ModelAttributeindicates that the argument should be retrieved from the model • If not present, the argument will be instantiated and added to the model • Then, the argument's fields will be populated from all request parameters that have matching names @RequestMapping(method = RequestMethod.POST) public String addUser( @ModelAttribute User user){ userService.addUser(user); return "redirect:users"; }

  37. Form Data Validation • A typical task for a web-application is to validate user data entered into a form • Spring MVC has its own solution for the data validation • Approach: • Implement the Validator interface • Invoke validator in a controller method • Implement custom Spring form on UI

  38. Implementing a Validator @Component public class UserValidator implements Validator { public boolean supports(Class<?> clazz) { return clazz.equals(User.class); } public void validate(Object target, Errors errors) { User user = (User)target; if (!StringUtils.hasText(user.getFirstName())){ errors.rejectValue("firstName", "error.empty", "Empty first name!"); } if (!StringUtils.hasText(user.getLastName())){ errors.rejectValue("lastName", "error.empty", "Empty last name!"); } } }

  39. Invoking a Validator @Controller@RequestMapping("/users") public class UsersController { @Autowiredprivate UserValidator validator; @RequestMapping(method = RequestMethod.POST) public String addUser(Model model, @ModelAttribute User user, BindingResult result){ validator.validate(user, result); if (result.hasErrors()) { // we'd like to display users anyway model.addAttribute("users", userService.getUsers()); return "users"; } userService.addUser(user); return "redirect:users"; } }

  40. Re-implementing a JSP form <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <form:form commandName="user" method="POST" action="/my-app/spring/users"> First name: <form:input path="firstName" /> <form:errors path="firstName" cssStyle="color: red;" /><br/> Last name: <form:input path="lastName" /> <form:errors path="lastName" cssStyle="color: red;" /><br/> <input type="submit" value="Add"/> </form:form> Special Spring tag library

  41. Instantiating a Command Object @RequestMapping(method = RequestMethod.GET) public ModelAndView displayUsers() { ModelAndView mav = new ModelAndView("users"); List<User> users = userService.getUsers(); mav.addObject("users", users); // empty command object for a form mav.addObject("user", new User()); return mav; } • JSP form now is associated with a command object named "user" • Need to explicitly instantiate it in a controller

  42. Validation Result

  43. URI Template Patterns • URI templates can be used for convenient access to selected parts of a URL in a@RequestMappingmethod • URI Template: http://www.example.com/users/{userId} contains the variable: userId @RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) public String findOwner(@PathVariable String ownerId, Model model) { Owner owner = ownerService.findOwner(ownerId); model.addAttribute("owner", owner); return "displayOwner"; }

  44. Template Patterns & Relative URLs @RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET) public String findPet(Model model, @PathVariable String ownerId, @PathVariable String petId) { Owner owner = ownerService.findOwner(ownerId); Pet pet = owner.getPet(petId); model.addAttribute("pet", pet); return "displayPet"; } Equivalent ways for implementing a handler for: /owners/42/pets/21 @Controller @RequestMapping("/owners/{ownerId}") public class RelativePathUriTemplateController { @RequestMapping("/pets/{petId}") public void findPet(Model model @PathVariable String ownerId, @PathVariable String petId) { // implementation omitted } }

  45. Customizing data binding It is possible to use@InitBinder to configure a CustomDateEditor for all java.util.Date form properties @Controller public class MyFormController { @InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); } }

  46. Interceptors • Useful when you want to apply specific functionality before/after certain requests • Contains twointerceptionmethod: preHandleunpostHandle • Contains onecallbackmethod: afterCompletion • Can be assigned to a set of controllers via configuration ofHandlerMapping

  47. Interceptor configuration <beans> <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method .annotation.RequestMappingHandlerMapping"> <property name="interceptors"> <list> <ref bean="officeHoursInterceptor"/> </list> </property> </bean> <bean id="officeHoursInterceptor" class="samples.TimeBasedAccessInterceptor"> <property name="openingTime" value="9"/> <property name="closingTime" value="18"/> </bean> <beans> Assign the interceptor to all requests

  48. Interceptor code public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { . . . public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Calendar cal = Calendar.getInstance(); int hour = cal.get(HOUR_OF_DAY); if (openingTime <= hour < closingTime) { return true; } else { response.sendRedirect( "http://host.com/outsideOfficeHours.html"); return false; } } }

  49. Resources Web MVC Framework documentation http://docs.spring.io/spring/docs/3.2.4.RELEASE/spring-framework-reference/html/mvc.html Tutorial "Designing and Implementing a Web Application with Spring" http://spring.io/guides/tutorials/web/ Spring MVC: Validator and @InitBinder http://fruzenshtein.com/spring-mvc-validator-initbinder/

More Related