500 likes | 846 Vues
Spring MVC. Cedric Dumoulin. Plan. Bibliographie Injecter des composants Spring et JEE Header Footer et Body Internationalization Validation Gestion des erreurs. Bibliographie. Spring Framework
E N D
Spring MVC Cedric Dumoulin
Plan • Bibliographie • Injecter des composants • Spring et JEE • Header Footer et Body • Internationalization • Validation • Gestion des erreurs
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_framework.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-30-Tutorial-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/
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") Déclare un composant /** * A simple bean that will be injected elsewhere */ @Component publicclassInjectedBean { private String firstname = "John"; private String lastname = "Doe"; //.. Getter an setter .. }
Injecter un composant • @Inject • Peut se faire sur un attribut, un setter, … • On peut spécifier le nom • @Inject("injectedComponent") @Controller publicclassHomeController { /** * Try to inject a bean */ @Inject protectedInjectedBeaninjectedBean; // .. } Spring injecte le bean du bon type
Webographie • 22.2 AccessingEJBs • http://docs.spring.io/spring/docs/4.0.0.BUILD-SNAPSHOT/spring-framework-reference/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 <?xmlversion="1.0" encoding="UTF-8"?> <beans:beansxmlns="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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"> <jee:remote-slsb id="myComponent" jndi-name="java:global/ipint13.springetejb.ear/ipint13.springetejb.domain/MyServiceBean" business-interface="ipint13.springetejb.domain.MyService"/> </beans:beans> • Dans le fichier de configuration la location de la def du namespace le nom spring l’interface du bean JEE le nom jndi est affiché par le serveur dans ses logs
Injecter le bean • Methode classique @Controller publicclassHomeController { @Inject protectedMyServiceinjectedBean; /** * 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-slsbid="myComponent" jndi-name="ejb/myBean" business-interface="com.mycom.MyComponent"/> <beanid="myController" class="com.mycom.myController"> <property name="myComponent" ref="myComponent"/> </bean>
Accéder à un objet JNDI ou EJBAutre methode • Permet d’utiliser le nom jndi directement dans les annotations • A tester <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"> <propertyname="alwaysUseJndiLookup" value="true" /> </bean>
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 + partie commune • Vous définissez le body • Le framework ce charge de construire la page en assemblant les parties pour vous
Webographie • 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/
Webographie • http://viralpatel.net/blogs/spring-3-mvc-internationalization-i18n-localization-tutorial-example/
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 • Pair 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 MVCLes 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" %> <%@tagliburi="http://www.springframework.org/tags" prefix="spring"%> <%@ page session="false" %> <html> <head> <title><spring:messagecode="page.home.title"/></title> </head> <body> <h1> <spring:messagecode="page.home.hello"/> </h1> <P><spring:messagecode="page.home.timeOnServer"/> ${serverTime}. </P> </body> </html> La taglib utilisée Le message
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:beanid="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <beans:propertyname="basename" value="classpath:messages" /> <beans:propertyname="defaultEncoding" value="UTF-8" /> </beans:bean>
Atelier • Créer un nouveau projet Spring. Spring génère une page « home ». Internationaliser cette page.
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-mvc-internationalization-i18n-localization-tutorial-example/
Documentation • http://docs.spring.io/spring/docs/3.2.5.RELEASE/spring-framework-reference/htmlsingle/#validation • 7.8.4 Spring MVC 3 Validation
Spring 3.x 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
Spring 3.x Erreur de conversion @RequestMapping(method = RequestMethod.POST) protected String onSubmit( @ModelAttribute("commandAjout") CommandAjoutcommandAjout, BindingResult result, SessionStatus status ) throws Exception { if( result.hasErrors()) { return"formulaire"; } groupe.addMembre(commandAjout.getNouveauMembre()); status.setComplete(); return"confirmation"; } • @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 plusieur s @ModelAttribute • BindingResult result contient les éventuelleserreurs de conversion • doitêtreplacéimmédiatement après le @ModelAttributeauquelil se réfere retourne au formulaire en cas d’erreurs efface la session si ok
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
Validation explicite /** Declare a validatorobject */ ValidatorcontactValidator = newContactValidator(); @RequestMapping(value = "/addContact2.html", method = RequestMethod.POST) public String addContact2(@ModelAttribute("command") Contact contact, BindingResultresult, Model model) { contactValidator.validate(contact, result); // Check the binding results. Binding and validations errors are contained // in the BindingResultobject. // If there is some binding or validation errors, stop and return // to the form. if( result.hasErrors()) { System.err.println("errorsencountered !!"); return"contact"; } … } Déclare un validator • Nécessite un objet validator appel la validation, Utilise le BindResult Verifie le résultat
Exemple de Validator publicclassContactValidatorimplementsValidator { /* * This Validatorvalidates *just Contact instances */ publicboolean supports(Class clazz) { returnContact.class.equals(clazz); } publicvoidvalidate(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"); } elseif (p.getAge() > 110) { e.rejectValue("age", "too.darn.old", "Age seem too old"); } } } (attributeName, errorCode, defaultMsg) errorCode i18n
Spring et @ValidPréparation • 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 protectedvoidinitBinder(WebDataBinder binder) { // register the ContactValidator used to validate objects of type Contact. binder.setValidator(newContactValidator() ); }
Spring et @ValidUtilisation /** Handler called when theform is submitted. * The @Valid annotation is used to validate the input model. Spring lookup for a * validatoraccepting the class. */ @RequestMapping(value = "/addContact.html", method = RequestMethod.POST) public String addContact(@Valid @ModelAttribute("command") Contact contact, BindingResultresult, Model model) { // Check the binding results. Binding and validations errors are contained // in the BindingResultobject. // If there is some binding or validation errors, stop and return // to the form. if( result.hasErrors()) { System.err.println("errorsencountered !!"); return"contact"; } … } Un validator pour ce type doit être accessible !
JSR-303 Bean validation API • Annotation JSR-303 • nécessite validation-api.jar • Nécessite une implémentation du standard ! • Les contraintes de validation sont directement dans l’objet à valider • Voir doc Spring
Documentation • 18.2 JSP & JSTL • http://docs.spring.io/spring/docs/4.0.0.BUILD-SNAPSHOT/spring-framework-reference/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 Errorest remplie par le Validator publicclassContactValidatorimplementsValidator { /* * This Validatorvalidates *just Contact instances */ publicboolean supports(Class clazz) { returnContact.class.equals(clazz); } publicvoidvalidate(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"); } elseif (p.getAge() > 110) { e.rejectValue("age", "too.darn.old", "Age seem too old"); } } } (attributeName, errorCode, defaultMsg) errorCode i18n
Le <form:form …> déclare où doivent être placé les messages d’erreurs <%@tagliburi="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:formmethod="post" action="addContact.html"> <table> <tr> <td><form:labelpath="firstname">First Name</form:label></td> <td><form:inputpath="firstname" /></td> <%-- Show errors for firstname field --%> <td><form:errorspath="firstname"/></td> </tr> <tr> <td><form:labelpath="lastname">Last Name</form:label></td> <td><form:inputpath="lastname" /></td> <%-- Show errors for lastname field --%> <td><form:errorspath="lastname"/></td> </tr> … Affiché uniquement si l’erreur existe !