Spring MVC

Transcription

Spring MVC
Cedric Dumoulin
Plan
 Bibliographie
 Header Footer et Body
 Internationalization (i18n)
 Validation
 Gestion des erreurs
 Injecter des composants
 Spring et JEE
Bibliographie
 Spring Framework
 http://docs.spring.io/spring/docs/3.2.5.RELEASE/spring
-framework-reference/htmlsingle/#overview
 Designing and Implementing a Web Application
with Spring
 http://spring.io/guides/tutorials/web/
Bibliographie
 Spring IO
 http://spring.io/
 Developing a Spring Framework MVC application
step-by-step (2.5)
 http://docs.spring.io/docs/Spring-MVC-step-by-step/
 Spring MVC Framework Tutorial
 http://www.tutorialspoint.com/spring/spring_web_mvc_fra
mework.htm
 Wikipedia
 http://en.wikipedia.org/wiki/Spring_Framework
 Quick start
 http://projects.spring.io/spring-framework/#quick-start
Bibliographie
 Spring 3.x tutorials
 http://www.roseindia.net/spring/spring3/index.shtml
 http://yannart.developpez.com/java/spring/tutoriel/
 http://www.theserverside.com/tutorial/Spring-30Tutorial-Setting-Up-Configuring-The-Environment
Guides
 Accessing Data with JPA
 http://spring.io/guides/gs/accessing-data-jpa/
 Designing and Implementing a Web Application
with Spring
 http://spring.io/guides/tutorials/web/
Rappel Spring MVC
serveur
applis
appli1
servlet1
select
appli2
appli3
ctrls
url=ctrl2
pages
view1
view1
select
view2
view2
url=ctrl3
view2
reponse
views
url=ctrl1
‘/’
select
‘appli1/ctrl1’
requete
servlets
view3
Rappel Spring MVC
serveur
applis
appli1
servlet1
select
appli2
appli3
ctrls
views
url=ctrl1
‘/’
select
‘appli1/ctrl1’
requete
servlets
viewResolver
déclaré ds springconfig.xml
url=ctrl2
pages
view1
view1
select
view2
view2
url=ctrl3
view3
view2
reponse
déclaré dans
web-inf
Dans une
Classe
JSP dans
WEB-INF
Tous les sites proposent
des pages cohérentes
 Avec un header, un footer, un body, un
menu …
 Faut-il coder ces éléments dans toutes les
pages ?
 Et si on doit modifier le footer, faut-il alors
modifier toutes les pages ?
 Solution :
 Utiliser un framework permettant de
separer le header, le footer, le body et le
menu …
 Ex: Tiles
Principe
 Définir une seule fois les parties communes:
 Un header, un footer un main menu
 Assembler les pages
 Seule le body change
  il faut coder celui-ci à part.
 Une page == body + parties communes
 Vous définissez le body
 Le framework ce charge de construire la page en
assemblant les parties pour vous
Mise en œuvre
layout1.jsp
header1.jsp
footer1.jsp
2) Assembler avec des définitions
3) choisir la ‘view’ dans le contrôleur
menu2.jsp
menu1.jsp
body1.jsp
body2.jsp
1) Définir des Tiles
En pratique
Master definition
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
"http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
<definition name=" classicLayout" template="/WEB-INF/views/layout1.jsp">
<put-attribute name="title" value="My Title"/>
<put-attribute name="header"
value="/WEB-INF/views/header1.jsp"/>
<put-attribute name="body" value=""/>
<put-attribute name="footer"
value="/WEB-INF/views/footer1.jsp"/>
</definition>
</tiles-definitions>
En pratique
View
<tiles-definitions>
<definition name= "view1" extends="classicLayout ">
<put-attribute name="title" value="Another Title"/>
<put-attribute name="body"
value="/WEB-INF/views/body1.jsp"/>
</definition>
</tiles-definitions>
Layout.jsp
(attention aux pieges  )
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%>
<!DOCTYPE html>
<html lang="en" class="no-js">
<head>
<meta charset="UTF-8">
<title><tiles:insertAttribute name="title" ignore="true" /></title>
<tiles:insertAttribute name="stylecss" />
</head>
<body>
<tiles:insertAttribute name="header" />
<tiles:insertAttribute name="body" />
<tiles:insertAttribute name="footer" />
<tiles:insertAttribute name="scriptjs" />
</body>
</html>
Configurer Spring
<!-- Resolves views selected for rendering
by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<beans:property name="definitions">
<beans:list>
<beans:value>/WEB-INF/tiles.xml</beans:value>
<beans:value>/WEB-INF/views/**/views.xml</beans:value>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="tilesViewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<beans:property name="viewClass">
<beans:value>
org.springframework.web.servlet.view.tiles3.TilesView
</beans:value>
</beans:property>
<beans:property name="order" value="0"/>
</beans:bean>
Ajouter les
dépendances
<!-- tiles -->
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-jsp</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-core</artifactId>
<version>3.0.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-api</artifactId>
<version>3.0.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-servlet</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-template</artifactId>
<version>3.0.3</version>
</dependency>
Spring MVC et Tiles
serveur
applis
appli1
servlet1
select
appli2
appli3
view1
url=ctrl2
pages
view2
url=ctrl3
view2
reponse
definitions tiles
ctrls
url=ctrl1
‘/’
select
‘appli1/ctrl1’
requete
servlets
lay
out
1.js
p
select
me
nu2
.jsp
bo
dy1
.jsp
hea
der1
foo
.jsp
ter
1.js
me
p
nu1.
jsp
bo
dy
2.js
p
Spring MVC et Tiles
serveur
applis
appli1
servlet1
select
appli2
appli3
definitions tiles
ctrls
url=ctrl1
‘/’
select
‘appli1/ctrl1’
requete
servlets
TilesViewResolver
déclaré ds springconfig.xml
view1
url=ctrl2
pages
lay
out
1.js
p
select
me
nu2
.jsp
bo
dy1
.jsp
view2
hea
der1
foo
.jsp
ter
1.js
me
p
nu1.
jsp
bo
dy
2.js
p
url=ctrl3
view2
reponse
déclaré dans
web-inf
Dans une
Classe
tiles.xml
fichiers jsp
sous
WEB-INF
Webographie
 Spring MVC 4 Tiles 3 Integration Tutorial
 http://frameworkonly.com/spring-mvc-4-tiles-3integration/
 Spring 3 MVC: Tiles Plugin Tutorial with Example in
Eclipse
 http://viralpatel.net/blogs/spring-3-mvc-tiles-plugin-
tutorial-example-eclipse/
 Tiles
 https://tiles.apache.org/
Utilisation dans le contrôleur
Spring
 Le contrôleur Spring doit renvoyer le nom logique de
la page a afficher
 C’est le nom spécifié dans le fichier tiles.xml lors de la
déclaration d’un assemblage …
Atelier
 ateliers 2 (i18n, valid).docx
 Header Footer Body (Tiles)
 Spring fournit la notion de composant
 On peut injecter des composants dans d’autre
composant
 Même principe que JEE
 Declarer un composant: @Component, @Named
 Injecter un composant:
 @Inject, @Autowired
Déclarer un composant
 Par défaut, le nom est le nom simple de la classe (commençant par une
minuscule)
 2 tags équivalent: @Component et @Named
 On peut spécifier le nom
 @Component("injectedComponent")
/**
* A simple bean that will be injected elsewhere
*/
@Component
public class InjectedBean {
private String firstname = "John";
private String lastname = "Doe";
//.. Getter an setter
}
..
Déclare un composant
Déclarer un composant (2)
 Indiquer a Spring de scanner les classes
 Dans le fichier de context
 <context:component-scan basepackage=‘’my.pacakge.base’’ />
Injecter un composant
 @Inject (et @Autowired, @Resource)
 Peut se faire sur un attribut, un setter, …



Recherche si il existe un bean du nom de l’attribut, et compatible avec le type
Si non recherche un bean compatible avec le type
Si non  erreur
 On ne peut pas spécifier le nom
 Sauf pour @Resource(‘name’)
@Controller
public class HomeController {
/**
* Try to inject a bean
*/
@Inject
protected InjectedBean injectedBean;
// ..
}
Spring injecte le bean du
bon type
Webographie
 22.2 Accessing EJBs
 http://docs.spring.io/spring/docs/4.0.0.BUILDSNAPSHOT/spring-frameworkreference/htmlsingle/#ejb
Injecter des EJB Session
dans Spring
 C’est possible !
 En deux temps:
 déclarer le bean Session en tant que Component Spring
 injecter le component Spring
Déclarer le bean session en tant
que Component
declaration du namespace
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
la location de la def du namespace
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation=
"http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">
 Dans le fichier de configuration
<jee:remote-slsb
id="myComponent"
jndi-name="java:global/ipint13.springetejb.ear/ipint13.springetejb.domain/MyServiceBean"
business-interface="ipint13.springetejb.domain.MyService"/>
</beans:beans>
le nom jndi est affiché par le serveur dans ses logs
le nom spring
l’interface du bean JEE
Injecter le bean
 Methode classique
@Controller
public class HomeController {
@Inject
protected MyService injectedBean;
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
// …
if( injectedBean == null ) {
logger.info("The bean is not injected !.");
return "home";
}
// Injection works !
model.addAttribute("myInjectedBean", injectedBean );
return "success";
}
Accéder à un objet JNDI ou EJB
 <jee:jndi-lookup>
 Acces par JNDI
 <jee:local-slsb>
 Acces a un bean local
 <jee:remote-slsb>
 Acces à un bean distant
<jee:local-slsb id="myComponent" jndi-name="ejb/myBean"
business-interface="com.mycom.MyComponent"/>
<bean id="myController" class="com.mycom.myController">
<property name="myComponent" ref="myComponent"/>
</bean>
Accéder à un objet JNDI ou EJB
Autre méthode
 Permet d’utiliser le nom jndi directement dans les
annotations
 A tester
<bean
class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
<property name="alwaysUseJndiLookup" value="true" />
</bean>
Atelier
 ateliers 2 (i18n, valid).pdf
 Spring et JEE
Webographie
 http://viralpatel.net/blogs/spring-3-mvc-
internationalization-i18n-localization-tutorialexample/
Principe
 Utilise la notion de « Locale » : Pays_langue
 FR_fr, US_en
 Dans le code, on utilise des constantes à la place du texte
 Les constantes sont définies dans des fichiers
 Pairs nom=valeur
 Un fichier par langue
 Un fichier par défaut
 Tous les fichiers ont le même nom

Mais des extensions en fonction du locale:
 messages.properties
 messages_FR_fr.properties
Alternatives:
 Des pages différentes en fonction du locale
 Possible avec Tiles
Spring MVC
Les fichiers properties
 Permet d’utiliser des constantes dans les pages
 Les définitions sont dans des fichiers .properties
fichier local = fr
fichier par défaut
pas définit = venant
du fichier par défaut
La déclaration dans la page
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ page session="false" %>
La taglib utilisée
<html>
<head>
<title><spring:message code="page.home.title"/></title>
</head>
Le message
<body>
<h1>
<spring:message code="page.home.hello"/>
</h1>
<P> <spring:message code="page.home.timeOnServer"/> ${serverTime}. </P>
</body>
</html>
La configuration
 Dans le fichier de configuration Spring
 [servlet]-config.xml
 Spécifier que l’on veut utiliser les messages de
properties
<!-- Specify the source for i18n -->
<beans:bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename" value="classpath:messages" />
<beans:property name="defaultEncoding" value="UTF-8" />
</beans:bean>
Laisser l’utilisateur
choisir sa langue
 Il faut ajouter un choix du local
 Il faut prendre en compte le changement
  voir tuto
 http://viralpatel.net/blogs/spring-3-mvcinternationalization-i18n-localization-tutorial-example/
Atelier
 Créer un nouveau projet Spring. Spring génère une
page « home ». Internationaliser cette page.
Documentation
 http://docs.spring.io/spring/docs/3.2.5.RELEASE/spri
ng-framework-reference/htmlsingle/#validation
 7.8.4 Spring MVC 3 Validation
 Tutorials
 http://www.codejava.net/frameworks/spring/sprin
g-mvc-form-validation-example-with-beanvalidation-api
Que valider ?
 Il faut valider pour se garantir :
• De mauvaises saisies dans les formulaires
• De données saisies non valables pour le métier
 Les mauvaises saisies peuvent être détectés par:
 la conversion de la requête http  objet command
 Les contraintes métiers peuvent être détectés par:
 des objets de validation
Validation
 Action de valider des données en fonction du métier
 ex: 0<= age <150
 Plusieurs possibilités avec Spring
 les technique se sont empilées avec le temps
 Les plus récentes:
 Validation explicite
 @Valid
 JSR-303 Bean Validation API
Spring 4 et la validation
 Deux méthodes (complémentaires)
 Erreurs de conversion

Détecté esquand Spring convertit les données du formulaire dans un
bean
 Bean Validation API
 a.k.a. JSR 303 for Bean Validation 1.0
 and JSR 349 for Bean Validation 1.1
 Fonctionne par annotations
 Actionnée à la demande (@Valid)
 Validator Objects
 Déclare et utilise des objets de validations
 Appel explicite du validator
Spring 3.x
Méthode 1
Erreurs de conversion
@RequestMapping(method = RequestMethod.POST)
protected String onSubmit(
@ModelAttribute("commandAjout") CommandAjout commandAjout,
BindingResult result, SessionStatus status ) throws Exception {
if( result.hasErrors()) {
return "formulaire";
}
retourne au formulaire en cas d’erreurs
groupe.addMembre(commandAjout.getNouveauMembre());
status.setComplete();
return "confirmation";
}
efface la session si ok
 @ModelAttribute permet de récupérer l’objet command.
 Il est peuplé à partir de la requete, donc avec les valeurs saisies dans le formulaire.
 Il y a conversion implicite String -> type dans l’objet commande
 Il peut y avoir plusieurs @ModelAttribute
 BindingResult result contient les éventuelles erreurs de conversion
 doit être placé immédiatement après le @ModelAttribute auquel il se réfere
Méthode 2
Bean Validation
 Principe:
 annote le bean
 Spécifie les messages d’erreurs
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
public class User {
@NotEmpty
@Email
private String email;
@NotEmpty(message = "Please enter your password.")
@Size(min = 6, max = 15, message = "Your password must between 6 and 15 characters")
private String password;
. . . // getters + setters
}
Validation explicite
lors de la conversion
@Controller
public class LoginController {
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String viewLogin(Map<String, Object> model) {
User user = new User();
model.put("userForm", user);
return "LoginForm";
}
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String doLogin(@Valid @ModelAttribute("userForm") User userForm,
BindingResult result, Map<String, Object> model) {
}
}
if (result.hasErrors()) {
return "LoginForm";
}
return "LoginSuccess";
Affichage des erreurs
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
...
<form:form action="login" commandName="userForm">
<tr>
<td align="left" width="20%">Email: </td>
<td align="left" width="40%"><form:input path="email" size="30"/></td>
<td align="left"><form:errors path="email" cssClass="error"/></td>
</tr>
<tr>
<td>Password: </td>
<td><form:password path="password" size="30"/></td>
<td><form:errors path="password" cssClass="error"/></td>
</tr>
<tr>
<td></td>
<td align="center"><input type="submit" value="Login"/></td>
<td></td>
</tr>
</form:form>
Librairie requises
 validation-api-1.1.0.Final.jar
 hibernate-validator-5.0.1.Final.jar
 Maven :
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.0.1.Final</version>
</dependency>
Méthode 3
Objet Validator
public class ContactValidator implements Validator {
Return vrai pour chaque type
pris en charge
( pour @Valid )
/*
* This Validator validates *just Contact instances
*/
public boolean supports(Class clazz) {
(attributeName, errorCode, defaultMsg)
return Contact.class.equals(clazz);
}
errorCode  i18n
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmptyOrWhitespace(e,
"firstname", "firstname.empty", "First Name is required");
ValidationUtils.rejectIfEmptyOrWhitespace(e,
"lastname", "lastname.empty", "Last Name is required");
Contact p = (Contact) obj;
if (p.getAge() < 0) {
e.rejectValue("age", "negativevalue", "Age should be >0");
} else if (p.getAge() > 110) {
e.rejectValue("age", "too.darn.old", "Age seem too old");
}
}
Validation explicite avec Validator
/** Declare a validator object */
Validator contactValidator = new ContactValidator();
 Nécessite un objet validator
Déclare un validator
@RequestMapping(value = "/addContact2.html", method = RequestMethod.POST)
public String addContact2(@ModelAttribute("command")
Contact contact, BindingResult result, Model model) {
appel la validation,
Utilise le BindResult
contactValidator.validate(contact, result);
// Check the binding results. Binding and validations errors are contained
// in the BindingResult object.
// If there is some binding or validation errors, stop and return
// to the form.
if( result.hasErrors()) {
Verifie le résultat
System.err.println("errors encountered !!");
return "contact";
}
…
}
Affichage des erreurs
 Même méthode que pécédemment
Appel automatique
des validators à la conversion
 Annotation JSR-303
 nécessite validation-api.jar
 Peut être utilisé avec Spring MVC
 nécessite de déclarer les validators (dans le controller par exemple)
/**
* Register a validator that will be lookup when a parameter is binded to a handler
* argument (with @ModelAttribute() for example).
* @param binder
*/
@InitBinder
protected void initBinder(WebDataBinder binder) {
// register the ContactValidator used to validate objects of type Contact.
binder.setValidator(new ContactValidator() );
}
Appel automatique des validators
Utilisation (@Valid)
/** Handler called when theform is submitted.
* The @Valid annotation is used to validate the input model. Spring lookup for a
* validator accepting the class.
*/
@RequestMapping(value = "/addContact.html", method = RequestMethod.POST)
public String addContact(@Valid @ModelAttribute("command")
Contact contact, BindingResult result, Model model) {
…
}
// Check the binding results. Binding and validations errors are contained
// in the BindingResult object.
// If there is some binding or validation errors, stop and return
// to the form.
if( result.hasErrors()) {
System.err.println("errors encountered !!");
return "contact";
}
Un validator pour ce type doit être
accessible !
Affichage des erreurs dans le formulaire
Documentation
 18.2 JSP & JSTL
 http://docs.spring.io/spring/docs/4.0.0.BUILDSNAPSHOT/spring-frameworkreference/htmlsingle/#view-jsp
Comment afficher
les erreurs dans la page ?
 Spring renvoie l’objet Errors dans la réponse
 Cet objet contient toutes les erreurs
 Chaque erreur est identifiées par un id

le nom de la propriété en général
 Le tag <form:errors …> utilise cet objet pour
afficher des messages
 On indique l’id de l’erreur à vérifier

le nom de la propriété en général …
L’objet Error est remplie
par le Validator (ou @Valid)
public class ContactValidator implements Validator {
/*
* This Validator validates *just Contact instances
*/
public boolean supports(Class clazz) {
(attributeName, errorCode, defaultMsg)
return Contact.class.equals(clazz);
}
errorCode  i18n
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmptyOrWhitespace(e,
"firstname", "firstname.empty", "First Name is required");
ValidationUtils.rejectIfEmptyOrWhitespace(e,
"lastname", "lastname.empty", "Last Name is required");
Contact p = (Contact) obj;
if (p.getAge() < 0) {
e.rejectValue("age", "negativevalue", "Age should be >0");
} else if (p.getAge() > 110) {
e.rejectValue("age", "too.darn.old", "Age seem too old");
}
}
Le <form:form …> déclare où doivent
être placé les messages d’erreurs
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<html>
<head>
<title>Spring 3 MVC Series - Contact Manager</title>
</head>
<body>
<h2>Contact Manager</h2>
<form:form method="post" action="addContact.html">
…
Affiché uniquement si
<table>
<tr>
<td><form:label path="firstname">First Name</form:label></td>
<td><form:input path="firstname" /></td>
<%-- Show errors for firstname field --%>
<td><form:errors path="firstname" /></td>
</tr>
<tr>
<td><form:label path="lastname">Last Name</form:label></td>
<td><form:input path="lastname" /></td>
<%-- Show errors for lastname field --%>
<td><form:errors path="lastname" /></td>
</tr>
l’erreur existe !
Atelier
 ateliers 2 (i18n, valid).pdf
 Formulaire et Validation

Documents pareils

Spring IO

Spring IO d’interpreter d interpreter les  annotations

Plus en détail