Les vues

Afficher une page avec Apache Tiles, JSP et Spring

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com

HTML permet facilement de concevoir une interface utilisateur pour des applications web. Cependant, sa gestion dans une application complexe peut poser de sérieux problèmes sans un support externe adéquat. C'est pourquoi dans notre application on implémentera des outils qui faciliteront la maintenance.

Les vues sous Spring avec Apache Tiles

Notre application de gestion de la bibliothèque utilisera Apache Tiles pour gérer les pages. Elles seront affichées en JavaServer Pages (JSP).

Apache Tiles est un framework des templates développé par Apache Software Foundation. Il permet au développeur de décomposer une page en plusieurs sous-parties. Ces sous-parties peuvent être ensuite intégrées sur les pages en fonction du besoin. Par exemple, dans notre cas, on veut que la page affichée en frontend contienne 4 sous-parties : un en-tête, une colonne de gauche, un contenu et un pied de page. Cela se traduit dans le code par :

< -- /WEB-INF/layout/tiles.xml -->
<tiles-definitions>
    <definition name="standardLayout" template="/WEB-INF/layout/standard.jsp">
        <put-attribute name="body" value="" />
        <put-attribute name="top" value="/WEB-INF/layout/top.jsp" />
        <put-attribute name="left" value="/WEB-INF/layout/left.jsp" />
        <put-attribute name="footer" value="/WEB-INF/layout/footer.jsp" />
    </definition>
    
    <definition name="register" extends="standardLayout">
        <put-attribute name="body" value="/WEB-INF/views/subscriber/register.jsp" />
    </definition>
    <definition name="registerSuccess" extends="standardLayout">
        <put-attribute name="body" value="/WEB-INF/views/subscriber/registerSuccess.jsp" />
    </definition>
    <definition name="registerConfirm" extends="standardLayout">
        <put-attribute name="body" value="/WEB-INF/views/subscriber/registerConfirm.jsp" />
    </definition>
    < -- they are more definitions, but all of them look like those 3 ones -->
</tiles-definitions>

On constate que ce système fonctionne à peu près comme l'héritage des classes en programmation orientée objet. Au tout début on a défini un layout de base (standardLayout). Il regroupe toutes les sous-parties qu'on voudra afficher par la suite sur la page. En bas on présente les 3 modèles de vue (attribut name) qui seront appelés ensuite par les contrôleurs pour afficher une page. Ces modèles surchargent l'attribut qui s'appelle body avec un fichier JSP spécifique à l'action effectuée. En occurrence, on a un template pour le formulaire d'enregistrement, une page pour la confirmation d'enregistrement et une autre pour la confirmation d'ouverture du compte.

Intégrer Apache Tiles dans Spring

Afin de pouvoir utiliser le système des templates sous Spring il faut configurer ViewResolver. Il s'agit d'une interface se trouvant dans le package org.springframework.web.servlet qui permet d'afficher les vues par leurs noms. Comme on a vu plus haut, les noms de nos vues sont définis dans la balise <definition />.

L'application qu'on développe utilise la classe UrlBasedViewResolver pour résoudre les vues. Elle contient les méthodes qui permettent de définir toute la configuration de la partie vues. On peut, par exemple, déterminer le suffixe ou le préfixe des templates ainsi que le type de contenu retourné. L'exemple utilisé ne comporte que la précision de la propriété viewClass. Elle comporte le nom de la classe employée pour créer des vues.

La classe utilisée pour la création des vues est TilesView. Il dépend du TilesContainer qui doit être disponible dans le contexte. Ce container est spécifié dans le bean nommé tilesConfigurer. Ce dernier contient la définition des modèles de vue utilisés par l'application. Voici l'exemple de la configuration des vues :

<bean id="viewResolver"
    class="org.springframework.web.servlet.view.UrlBasedViewResolver">
    <property name="viewClass">
        <value>org.springframework.web.servlet.view.tiles2.TilesView</value>
    </property>
</bean>
	<!-- Initializes the Apache Tiles CompositeView system -->
	<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
		<property name="definitions">
			<value>/WEB-INF/layout/tiles.xml</value>
		</property>
	</bean>

JSP et taglibs dans Spring

Dans l'article introductif au JSP dans les applications web, on a expliqué les standards du codage ainsi que les bénéfices qu'on peut tirer de cette technologie. On a également abordé le concept des JSP Standard Tags Library qui aide, entre autres, à créer les vues dynamiques contenant des boucles, des instructions conditionnelles etc.

Spring et ses projets possèdent leurs propres librairies des tags. Le projet Spring Security, qu'on va examiner dans les prochains articles, contient sa librairie des tags qui permet de récupérer les identités de l'utilisateur connecté. Un autre taglib, celui de l'Apache Tiles évoqués précédemment, permet par exemple d'insérer une sous-partie dans un fichier de vue. Voici l'exemple qu'on retrouvera dans notre application :

< -- standard.jsp -->
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
< -- other code ommitted -->
    <tiles:insertAttribute name="top" />
< -- other code ommitted -->

Pour vulgariser ce code, on peut dire qu'insertAttribute est une fonction de la "classe" tiles, qui prend comme paramètre name<. La "classe" est chargée au début du document, grâce à la balise taglib.

Spring possède aussi quelques tags propres qui accélèrent le développement. On peut, par exemple, facilement récupérer les messages traduits en fonction de la Locale de l'utilisateur. Cela se fait de la manière suivante :

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<spring:message code="user.label.login" var="labelLogin"/>
<spring:message code="user.label.password" var="labelPassword"/>
<spring:message code="user.label.passwordRepeat" var="labelPasswordRepeat"/>
<spring:message code="user.label.email" var="labelEmail"/>
<spring:message code="user.register.error" var="registerError"/>

< -- use the messages like that ${labelEmail} ${registerError} etc. -->

Le fonctionnement est simple. Spring va chercher le message placé dans notre fichier de traduction déterminé par le valeur de l'attribut code. Ensuite on définit le nom de la variable qu'on pourra utiliser plus loin dans le template (attribut var).

D'autres tags existent. L'un d'entre eux permet de créer l'URL. Dans notre cas, il sera employé pour charger les fichiers CSS et JS :

    <script type="text/javascript" src="<c:url value="/public/js/jquery-1.8.3.min.js" />"></script>
    <link type="text/css" rel="stylesheet" href="<c:url value="/public/css/main.css" />" />	

Un troisième tag utilisé est eval. Il sert à évaluer Spring expression (SpEL). Dans notre exemple on l'employera dans le formatage d'une date :

< -- doBooking => /WEB-INF/views/booking/doBooking.jsp -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 

<input type="text" name="bookingDate" value="<spring:eval expression="booking.bookingDate" />" />

Dans beaucoup d'endroits, notre système de gestion de la bibliothèque contient un taglib à qui on consacrera un article séparé. Il s'agit du form. Ce tag sera abordé dans la partie consacrée à des formulaires dans Spring.

Transmettre une valeur à la vue

La couche vue résulte d'une action effectuée par le contrôleur. C'est donc ce dernier qui manipuler les variables qui seront ensuite affichées dans le navigateur. Comment donc transmettre ces variables à la vue ? Analysons cela sur la page qui affiche un livre :

// imports ommitted
@RequestMapping("/books")
@Controller
public class BookController extends FrontendController {
    // other code ommitted
    
    @RequestMapping(value = "{category}/{title}/{id}", method = RequestMethod.GET)
    public String showBook(@PathVariable String category, @PathVariable String title, @PathVariable long id, Model layout, HttpServletRequest request, HttpServletResponse response) {
        // ...
        List<Book> book = bookService.getByIdAndLang(id, lang);
        logger.info("====> found book instance " + book);
        layout.addAttribute("book", book);
        // ...
        return "showBook";
    }
}
// showBook => /WEB-INF/views/book/showBook.jsp
<h1>${book[0][0]}</h1>

On constate donc qu'on peut récupérer l'instance de la classe Book via un simple ${book}. Le passage de cette valeur se fait par layout.addAttribute("book", book) où le premier paramètre signifie le nom d'objet utilisé par le template.

Tout objet peut être passé au template. Cependant le traitement de certains est spécifique. C'est le cas pour une liste, où l'on retrouve les objets par rapport à des index déterminés, comme dans PHP. Pour retrouver donc le premier élément de la liste books, on mettra dans le template le fragment suivant : ${book[0]}.

Gestion des exceptions sous Spring

Spring permet d'appeler les vues en fonction des exceptions lancées. On peut, par exemple, vouloir que chaque exception liée au transfert d'un fichier trop grand, invoque la vue "fileTooBigError". La définition de la relation entre l'exception et la vue se fait à travers SimpleMappingExceptionResolver. Voici l'exemple d'une configuration :

    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
	    <property name="exceptionMappings">
	        <props>
	            <prop key="org.springframework.web.multipart.MultipartException">fileTooBigError
	        </props>
	    </property>
	    <property name="defaultErrorView" value="defaultException" />
	</bean>

Dans le code ci-dessus on remarque la présence de la propriété exceptionsMappings qui regroupe à lui tout seul les exceptions gérées par SimpleMappingExceptionResolver. Si une exception est absente dans cette liste des propriétés, elle sera gérée par la vue spécifiée dans defaultErrorView.

Mais attention, uniquement les exceptions lancées par la partie handler (donc par les contrôleurs), seront traitées par ce resolver. SimpleMappingExceptionResolver implémente l'interface HandlerExceptionResolver. Comme indique le nom de cette dernière, seulement les exceptions du handler peuvent être gérées.

Une question ? Une remarque ?

*

*

Moi

Développeur d'applications Internet et journaliste passionné par l'adjectif français. Un aigle polonais orienté vers la progression, volant très haut et écoutant du zouk après les matches du foot français.

Vous appréciez mon travail ?

Pour contribuer au développement de ce site, ou pour remercier pour des articles rédigés, vous pouvez faire un don.

Un conseil MySQL

Comment importer un fichier SQL depuis la console ?

L'importation des fichiers .sql se déroule avec la commande suivante :

mysql -u root --password=root -h 127.0.0.1 -D myDatabase < tab_to_import.sql