image/svg+xml $ $ ing$ ing$ ces$ ces$ Res Res ea ea Res->ea ou ou Res->ou r r ea->r ch ch ea->ch r->ces$ r->ch ch->$ ch->ing$ T T T->ea ou->r

A propos des frameworks web et de JSF en particulier...

Une application web se caractérise par une interface graphique exposée à l'utilisateur basée sur des pages HTML enrichies avec l'utilisation de JavaScript :

Les langages HTML5/CSS/JavaScript sont suffisants pour réaliser des applications complètes autonomes ou interfacées avec des services web fournissant des données avec l'approche AJAX. Ainsi des frameworks favorisent l'écriture d'applications centrées principalement sur le client web (navigateur) :

Le principal intérêt d'une approche centrée sur le client est qu'elle promeut l'écriture de services web qui peuvent être réutilisés dans d'autres contextes : par exemple utilisées par d'autres types d'interfaces graphiques (clients lourds n'utilisant pas une interface web tel qu'application native Android) ou par des scripts.

Un autre type d'approche consiste à laisser au serveur web la primauté de la gestion de l'application ; les tâches réalisées par le client au sein de l'interface web peuvent être vues comme une simple délégation de travail. Il existe des frameworks web orienté serveur pour la plupart des langages les plus populaires :

La plupart des frameworks web modernes tendent à promouvoir la séparation entre la description de la vue à afficher, le code métier et le code mettant en relation la partie métier avec la vue : il s'agit ainsi de mettre en oeuvre le paradigme Modèle-Vue-Contrôleur (MVC). Les frameworks web plus anciens étaient moins rigoureux quant à la séparation de ces différentes couches : il est ainsi possible en utilisant Java Server Pages (JSP) de mélanger dans une page à la fois du code HTML et du code Java exécuté côté serveur.

Le paradigme MVC peut être mis en oeuvre de différentes manières ; on distingue deux approches divergentes à ce sujet :

La plupart des frameworks web embrassent différents aspects de la réalisation d'applications web ; ils mettent généralement en place les mécanismes suivants :

JSF repose principalement sur l'usage des API standard de Java EE à savoir JPA pour la persistance des objets (dont on peut utiliser l'implantation Hibernate par exemple). L'injection de dépendances est géré avec les Enterprise Java Beans et CDI ; le système de validation de beans peut être également utilisé pour valider les entrées utilisateur. JSF propose le système de templates Facelets.

Pour en savoir plus à propos de JSF :

FacesServlet

Voici un exemple de fichier web.xml pour mettre en place FacesServlet. Toutes les URIs de la forme jsf/*.xhtml pointeront vers FacesServlet :

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
	<context-param>
		<param-name>javax.faces.PROJECT_STAGE</param-name>
		<param-value>Development</param-value>
	</context-param>
	
	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup> <!-- we force the loading of the servlet at startup -->
	</servlet>
 
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>/jsf/*.xhtml</url-pattern>
	</servlet-mapping>
</web-app>

Modèle

Le modèle de l'application est géré à l'aide d'un ou plusieurs beans. De tels beans peuvent eux-mêmes référencer des beans entités correspondant à des données persistantes.

Une classe beans utilisée par JSF est annotée avec 0ManagedBean. On peut également spécifier sa portée (cf portées des beans), la portée la plus pertinente étant généralement @ConversationScoped qui est à mi-chemin entre @RequestScoped (portée limitée à la requête courante) et @SessionScoped (portée limitée à la session courante). Le concept de conversation représente un morceau de session correspondant à une succession de requêtes correspondant à un dialogue avec le serveur. JSF maintient donc l'instance du bean durant toute cette conversation. JSF introduit donc une notion de bean avec conservation d'état par dessus le protocole HTTP qui est par nature sans état.

Un exemple typique de modèle pourrait être le contenu entré par l'utilisateur sur un formulaire web découpé en plusieurs pages. Cela pourrait être le cas par exemple pour un processus de finalisation d'achat sur un site e-commerce avec des pages pour valider le panier, choisir son mode de livraison, entrer son adresse de facturation, son adresse de livraison, choisir son mode de paiement et entrer ses coordonnées bancaires. Toutes ces informations peuvent être regroupées dans une bean Order.

Vue avec facelets

Historiquement, JSF proposait l'utilisation de pages JSP (Java Server Pages) comme mode de création de vue. Cette technologie a été abandonnée au profit des facelets qui permettent une meilleure séparation entre code de contrôle et présentation des données. Les facelets ont êté largement inspirées par les concepts introduits par Apache Tapestry.

La composition des templates

Facelets permet de définir des templates pour la vue qui peuvent être utilisés individuellement ou être composés ensemble, voire paramétrés. Nous pouvons ainsi factoriser des portions de vue similaires que l'on peut réutiliser dans des contextes différents.

Par exemple, la plupart des sites web comportent généralement un en-tête de page contenant le titre et le menu du site ainsi qu'un pied de page. Nous pouvons séparer ces différentes sections au sein de différents templates.

Considérons pour exemple le site web d'un zoo. Nous créons d'abord l'en-tête de page avec le menu dans un fichier templates/header.xhtml ; nous pouvons utiliser du code HTML normal en entourant par une balise <ui:insert name=".."> les emplacements à remplacer par du contenu spécifique :

<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://xmlns.jcp.org/jsf/facelets">

	<body>
		<ui:composition>
			<div class="logo" style="font-size:34pt">JavaZoo</div>
			<div class="menu">
				Zoo residents :
					<a href="/aslan.xhtml">Aslan the lion</a> |
					<a href="/dumbo.xhtml">Dumbo the elephant</a>     |
					<a href="/kingkong.xhtml">King-Kong the gorilla</a>
			</div>
			
			<hr />
			
			<h1>
				<ui:insert name="title">Default title (to be replaced)</ui:insert>
			</h1>
		</ui:composition>
	</body>
</html>

On écrit maintenant le template templates/footer.xhtml pour le pied de page :

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
	<hr />
	<div class="footer_links">
		<a href="/contact.xhtml">Contact us</a> |
		<a href="/guestbook.xhtml">Sign the guestbook</a>
	</div>
</ui:composition>

Nous crééons maintenant un template représentant une page stéréotypique concernant un animal résident du zoo dans templates/resident.xhtml :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html">
      
    <h:head>
    	<title>#{animalName}</title>
    </h:head>
      
  	<h:body>
			<ui:decorate template="/templates/header.xhtml">
				<ui:define name="title">#{animalName} the #{animalSpecies}</ui:define>
			</ui:decorate>
			
			<p>Welcome on the webpage of #{animalName} !</p>
			
			<!-- Display a debug component on the page -->
			<ui:debug />
			
			<ui:insert name="picture">Sorry but no picture of #{animalName} is available</ui:insert>
			
			<ui:insert name="presentation">
				<p>Hello, my name is #{animalName} and I'm a #{animalSpecies}. I'm born in the year #{animalBirthYear}.</p>
			</ui:insert>
			
			<ui:include src="/templates/footer.xhtml" />
	</h:body>
</html>

On peut maintenant créer des pages spécifiques pour chaque animal du zoo, par exemple ici pour Aslan :

<ui:composition template="templates/resident.xhtml"
 xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
 xmlns:h="http://java.sun.com/jsf/html">
	<ui:param name="animalName" value="Aslan" />
	<ui:param name="animalSpecies" value="Lion" />
	<ui:param name="animalBirthYear" value="1980" />
	
	<ui:define name="picture">
		<h:graphicImage value="lion.jpg" library="images" />
	</ui:define>
</ui:composition>

En conclusion :

Astuce :

La gestion des ressources et l'i18n

Ressources textes

# strings used by our website
animalCompleteName = {0} the {1}
zooResidents = Zoo residents
lion = lion
elephant = elephant
gorilla = gorilla
contactUs = Contact us
signTheGuestbook = Sign the guestbook

<faces-config ..>
	<application>
		<resource-bundle>
			<base-name>mystrings</base-name>
			<var>strings</var>
		</resource-bundle>
</faces-config>

<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://xmlns.jcp.org/jsf/facelets">

	<body>
		<ui:composition>
			<div class="logo" style="font-size:34pt">JavaZoo</div>
			<div class="menu">
				#{strings.zoo_residents} :
					<a href="/aslan.xhtml">
						<h:outputFormat value="#{strings.animalCompleteName}">
							<f:param value="Aslan"/>
							<f:param value="#{strings.lion}"/>
						</h:outputFormat>
					</a>
			</div>
			
			<hr />
			
			<h1>
				<ui:insert name="title">Default title (to be replaced)</ui:insert>
			</h1>
		</ui:composition>
	</body>
</html>

<f:view locale="#{facesContext.externalContext.requestLocale}">
	...
</f:view>

Internationalisation de pages

Voici le code du managed bean :

@ManagedBean(name="internationalizedPageLoader")
@RequestScoped
public class InternationalizedPageLoader 
{
	public String getResource(String path, Locale l) throws IOException
	{
		ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
		for (String suffix: new String[]{l.getLanguage() + "_" + l.getCountry(), l.getLanguage()})
		{
			String newPath = path.replace(".xhtml", String.format(".%s.xhtml", suffix));
			URL url = context.getResource(newPath);
			if (url != null)
				return newPath;
		}
		return null;
	}
	
	public void loadPage() throws IOException
	{
		FacesContext context = FacesContext.getCurrentInstance();
		ExternalContext external = context.getExternalContext();
		Iterator<Locale> it = context.getExternalContext().getRequestLocales();
		String path = context.getViewRoot().getViewId();
		while (it.hasNext())
		{
			String newPath = getResource(path, it.next());
			if (newPath != null)
			{
				external.redirect(external.getRequestContextPath() + newPath);
				return;
			}
		}
		String newPath = getResource(path, Locale.ENGLISH);
		if (newPath != null)
			external.redirect(external.getRequestContextPath() + newPath);
		else
			external.setResponseStatus(HttpServletResponse.SC_NOT_FOUND);
	}
}

Et le code du template principal :

<f:metadata xmlns:f="http://xmlns.jcp.org/jsf/core">
    <f:event type="preRenderView" listener="#{pageLoader.loadPage}"/>
</f:metadata>

Il ne reste plus qu'à écrire des templates spécifiques pour les différentes langues (template.en.xhtml, template.fr.xhtml).

ResourceHandler

<application>
	<resource-handler>fr.upem.MyResourceHandler</resource-handler>
</application>

L'utilisation d'un ResourceHandler une gestion automatisée et centralisée des ressources multimédia telles que les images, les scripts JavaScript ainsi que les feuilles de style. Nous pouvons à cet effet utiliser les balises suivantes :

WebContent/
	resources/
		mytheme/
			css/
				style.css
			js/
				script.js
			img/
				logo.png

Formulaire et beans

Les facelets peuvent faire appel à des managed beans afin de s'interfacer avec le code métier de l'application. On peut ainsi communiquer bidirectionnellement entre la vue (la page HTML) et le managed bean hébergé sur le serveur :

Quelques conseils pour la création de managed beans :

À titre d'exemple, implantons un livre d'or qui gère une liste de messages (chaînes de caractères et auteur) envoyés par les visiteurs d'un site.

Voici le template (vue) utilisé :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html">
      
    <h:head>
    	<title>Guestbook</title>
    </h:head>
      
  	<h:body>
			<ui:decorate template="/WEB-INF/templates/header.xhtml">
				<ui:define name="title">Post a message on the guest book</ui:define>
			</ui:decorate>
			
			<h:form>
				<h:messages styleClass="messageCssStyle" globalOnly="true" showDetail="true" />
				
				<h:panelGrid columns="3">
					<h:outputLabel for="name" value="Name" /> <h:inputText id="name" value="#{guestbookBean.message.name}"  /> <h:message for="name" />
					<h:outputLabel for="email" value="Email" /> <h:inputText id="email" value="#{guestbookBean.message.email}"  /> <h:message for="email" />
					<h:outputLabel for="body" value="Body" /> <h:inputTextarea id="body" value="#{guestbookBean.message.body}"  cols="80" rows="10" /> <h:message for="body" />
				</h:panelGrid>
				
				<h:commandButton action="#{guestbookBean.submit}" value="Submit" />
			</h:form>
			
			<ui:include src="/WEB-INF/templates/footer.xhtml" />
	</h:body>
</html>

Ainsi que le bean CDI gérant le livre d'or :

@RequestScoped
@Named("guestbookBean")
public class GuestbookBean 
{
	@ConversationScoped
	public static class Message implements Serializable
	{
		private static final long serialVersionUID = 1L;
		
		private String name;
		private String email;
		private String body;
		
		public String getName() { return name; }
		public void setName(String name) { this.name = name; }
		
		public String getEmail() { return email; }
		public void setEmail(String email) { this.email = email; }
		
		public String getBody() { return body; }
		public void setBody(String body) { this.body = body; }
	}
	
	@ApplicationScoped
	public static class Guestbook
	{
		private List<Message> messages = new ArrayList<>();
		
		public List<Message> getMessageList() { return messages; }
	}
	
	@Inject private Message message;
	
	@Inject private Guestbook guestbook;
	
	public Message getMessage()
	{
		return message;
	}
	
	public Guestbook getGuestbook()
	{
		return guestbook;
	}
	
	public String submit()
	{
		guestbook.getMessageList().add(message);
		FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Message added", 
			"Your message was successfully added in the guestbook, the guestbook contains now " + getGuestbook().getMessageList().size() + " messages")); 
		return null;
	}
}

Une architecture plus modulaire utiliserait des beans EJB et CDI :

À titre d'exercice, on pourra essayer d'implanter un livre d'or persistant stockant les messages dans une base de données plutôt que la mémoire (en utilisant l'API JPA) en implantant les beans précédemment décrits.

Validation des données

La validation des données communiquées dans un formulaire peut être réalisée à deux niveaux différents :

Dans la mesure du possible, on essaiera de réaliser la validation au plus bas niveau possible, i.e. des beans entités car le mécanisme mis en oeuvre sera réutilisable potentiellement avec des technologies autres que JSF s'occupant de la couche vue.

Validation des données de la vue par JSF

Les templates de déclaration de vue peuvent intégrer des balises spécifiant des contraintes de valeur pour les champs (comme sous-balises des champs). L'attribut for est commun à toutes ses balises et permet d'indiquer l'identifiant du composant concerné par la contrainte de validation.

Voici quelques balises de contraintes de validation :

Nom de la balise Propriétés spécifiques
<f:validate{Double,Long}Range /> minimum="minValue", maximum="maxValue"
<f:validateLength /> minimum="minLength", maximum="maxLength"
<f:validateBean />
<f:validator /> binding="validator"
<f:validateRegex /> pattern="regularExpression"
Exemple d'utilisation de validateRegex

On peut utiliser validateRegex pour vérifier une adresse email :

<h:inputText id="email"
	value="#{message.email}" 
	required="true"
	requiredMessage="You must enter an email address"
	validatorMessage="The entered email address is not acceptable (invalid or too long)">
		<f:validateRegex pattern="^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,63}$" />
		<f:validateLength maximum="128" />
</h:inputText>

Nous avons également ajouté un test sur la longueur de l'adresse afin qu'elle ne dépasse pas 128 caractères.

Plutôt que d'utiliser une chaîne de caractères en dur, il est recommandé d'utiliser une chaîne provenant d'un ResourceBundle pour pouvoir internationaliser les messages.

Exemple d'utilisation de validator

Il est possible d'implanter un validateur JSF personnalisé : cela peut être utile par exemple afin de tester si deux champs ont les mêmes valeurs. On peut ainsi demander à l'utilisateur de fournir deux fois le contenu d'un champs afin de vérifier qu'il n'y a pas d'erreur de saisie (comme une adresse email ou un mot de passe).

Voici un extrait de template avec deux champs pour l'adresse email :

Email address: <h:inputText id="email" ...>
	....
</h:inputText>
Email address confirmation: 
<h:inputText id="email2"
	required="true"
	requiredMessage="You must confirm the email address"
	validatorMessage="Confirmed email address not the same than the initial address">
	<f:validator binding="duplicateFieldValidator" />
	<f:attribute name="referenceField" value="#{email}" />
</h:inputText>

Nous devons maintenant implanter un bean chargé de réaliser la validation :

@FacesValidator( value = "duplicateFieldValidator" )
public class DuplicateFieldValidator implements Validator
{
    @Override
    public void validate( FacesContext context, UIComponent component, Object value ) throws ValidatorException 
    {
	// we retrieve the content of the confirmation field
        UIInput referenceComponent = (UIInput) component.getAttributes().get("referenceField");
        String referenceInput = referenceComponent.getSubmittedValue();
        // and we test the equality between the reference field and the confirmation field inputs
        if (! referenceInput.equals(value.toString())
		throw new ValidatorException(new FacesMessage("Inconsistency between the content of the two fields"));
    }
}

Validation par un bean

Truc : si l'on souhaite que les chaînes de caractères vides de formulaire soient assimilées à des chaînes null, on peut ajouter cette option dans le fichier web.xml :

<context-param>
    <param-name>
        javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL
    </param-name>
    <param-value>true</param-value>
</context-param>

Conversion

Les valeurs des champs étant envoyées sous la forme de chaînes de caractères, quelquefois une conversion du champ peut être nécessaire dans le type déclaré (int, long, float, double, boolean...). On peut ajouter au niveau du champ de formulaire un attribut converterMessage qui est utilisé pour afficher un message d'erreur si la conversion n'est pas possible. Pour les types courants, le client pourra également réaliser la vérification de son côté (par exemple pour n'autoriser que l'entrée de chiffres pour un champ représentant un entier).

Retour de messages

Si la validation de champs a échoué il est nécessaire d'en informer l'utilisateur. Il existe deux façons de réaliser la validation pour informer l'utilisateur : au niveau du client ou du serveur.

Validation au niveau du client

La validation côté-client n'est pas proposée en standard par JSF. Certains implantations proposent cependant en complément ce mécanisme. Il s'agit d'exécuter un script JavaScript chargé de vérifier le contenu de champs de formulaire (à chaque caractère entré ou en changement de champ). L'utilisateur est informé immédiatement d'un éventuel problème de saisie : cela est plus rapide et limite les communications avec le serveur. La validation côté client doit toujours être utilisée en complément de la validation côté-serveur et jamais en substitution : le navigateur peut ne pas exécuter le code JavaScript pour différentes raisons (vieux navigateur, JavaScript désactivé, soumission de formulaire par un script...). On notera que la validation côté-client ne fonctionne que pour les contraintes prédéfinies (étendue de valeur, respect d'une expression régulière...) ; les contraintes plus complexes exécutant du code Java arbitraire ou consultant des données du serveur ne peuvent être mises en oeuvre ; le développeur peut toutefois indiquer lui-même du code JavaScript réalisant la validation.

Par exemple l'implantation RichFaces de JBoss propose une balise <rich:validator /> à incorporer comme sous-balise : pour plus d'informations on pourra se référer à cette page. Un attribut event peut être utilisé pour cette balise pour indiquer si l'on souhaite déclencher la validation au changement de champ (change) ou à chaque caractère entré (keyup).

Validation au niveau du serveur

Par défaut la validation a toujours lieu côté serveur. Cela nécessite l'envoi complet du formulaire. Il faut intégrer sur la page un affichage de messages d'erreurs globaux pour la validation ainsi que pour chaque champ individuel. Ainsi l'utilisateur peut être informé des erreurs de saisie.

Il est également possible de réaliser des validations champ par champ côté serveur en utilisant un appel AJAX : le seul contenu du champ est envoyé au serveur et celui-ci répond en indiquant les erreurs rencontrées ou non. Ce mécanisme est mis en oeuvre automatiquement. Voici un exemple :

<h:inputText id="email" value="#{message.email}"
required="true"
requiredMessage="{messages.emailAddressRequired}"
validatorMessage="{messages.emailAddressInvalid}">
	<f:validateRegex pattern="^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,63}$" />
	<f:validateLength maximum="128" />
	<f:ajax event="blur" render="sessionidMessage" />                                          
</h:inputText>
<h:message id="emailMessage" for="email" />

Il ne faut pas oublier de spécifier une balise message afin d'indiquer l'emplacement où doit être affiché le message d'erreur. Si le navigateur ne réalise pas d'appel AJAX, la validation sera de toute façon réalisée globalement lors de la soumission du formulaire.

Navigation

Utilisation de liens hypertextes

L'impression d'un lien hypertexte est possible avec la balise h:outputLink, l'URL du lien étant indiquée avec l'attribut value. Il est possible de rajouter des paramètres au lien qui sont ajoutés et encodés correctement dans la section query de l'URL. Voici un exemple pour afficher un lien recherchant le contenu de l'attribut keywords de mybean sur Google :

<h:outputLink id="googleLink" value="http://www.google.com/">
	<f:param name="q" value="#{mybean.keywords}" />
	<h:outputText value="Search keyword" />
</h:outputLink>

h:outputLink est particulièrement adapté pour des liens sur des sites externes. Pour des liens internes, déterminer la valeur du lien à indiquer peut-être problématique car on ne connaît pas nécessairement le contexte d'installation de l'application web (avec notamment le mapping entre nom de template et URL). On utilisera donc plutôt dans ces conditions la balise h:link qui prend un attribut outcome correspondant au nom du template à afficher.

<h:link id="contactLink" outcome="contact" value="Contact us" />

L'exemple précédent affiche un lien affichant le texte "Contact us" (qu'on pourra remplacer par une expression EL pour i18n) nous permettant de nous rendre sur le template contact.

Notons que les liens hypertextes impliquent obligatoirement l'envoi d'une requête HTTP avec une méthode GET : ils sont donc adaptés pour la navigation de page en page mais pas pour envoyer des données (on utilisera plutôt à cet effet des formulaires).

Il est possible de communiquer des arguments à un template de notre site depuis un lien hypertexte réalisant une requête GET. En reprenant l'exemple précédent d'un lien de contact, nous pouvons indiquer le service que l'on souhaite contacter (avec son adresse email, quoique cela soit une mauvaise idée pour limiter le spam) :

<h:link id="contactLink" outcome="contact" value="Contact the webmaster">
	<f:param name="email" value="postmaster@example.com />
</h:link>

<h:link id="contactLink" outcome="contact" value="Contact the CEO"">
	<f:param name="email" value="ceo@example.com />
</h:link>

On peut ensuite indiquer dans une section <f:metadata> les paramètres que l'on souhaite récupérer (ici l'adresse email de contact) :

<f:metadata>
	<f:viewParam name="email" value="#{contactBean.emailAddress}" />
</f:metadata>

Cela a pour conséquence d'appeler la méthode setEmailAddress() de contactBean en lui passant en argument le paramètre email AVANT le rendu de la vue. Ainsi dans la vue, nous pourrons utiliser l'expression EL #{contactBean.emailAddress} pour récupérer cette adresse, en la plaçant par exemple dans un champ caché de formulaire de contact :

<h:form>
	<h:inputHidden id="email" value="#{contactBean.email}" />
	<h:inputTextarea id="content" value="#{contactBean.content}" />
	<h:commandButton value="Submit" action="#{contactBean.send}" />
</h:form>

Envoi de formulaires

L'envoi d'un formulaire doit normalement se réaliser par le clic sur un bouton. On utilise la balise h:commandButton à cet effet comme pour l'exemple suivant :

<h:form>
	<h:inputText id="input" value="#{myBean.input}" />
	<h:commandButton value="Submit" action="#{indexBean.submit}" />
</h:form>

Il est possible de créer un bouton affichant une image avec l'attribut image de h:commandButton. L'attribut action du bouton spécifie par une expression EL la méthode à appeler lors de la soumission du formulaire. Cette méthode n'est appelée que si la validation de tous les champs du formulaire réussit : dans le cas contraire, la vue courante est réaffichée avec d'éventuels messages d'erreur d'accompagnement.

La méthode appelée par l'attribut action ne doit pas prendre d'argument et retourner un String indiquant le nom du prochain template à afficher. La chaîne peut également être null : dans ce cas, le template courant est rechargé.

L'envoi d'un formulaire peut également être réalisé avec un h:commandLink mais ceci est déconseillé car cela implique l'utilisation de code JavaScript.

La soumission d'un formulaire en utilisant une requête AJAX est possible : cela permet de conserver la vue courante en mettant à jour uniquement certaines sections de celle-ci.

Utilisation des requêtes/réponses

ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
HttpServletRequest request = (HttpServletRequest)context.getRequest();
HttpServletResponse response = (HttpServletResponse)context.getResponse();