Archives pour la catégorie Play

Play – Exécuter un template depuis un Controller (render, renderTemplate, …)

Dans le tutorial précédent, on a vu qu’un Controller Play peut renvoyer différents type de données grâce notamment à des méthodes spécialisées comme renderJSON ou renderBinary:

Play – Controller et les variantes de render() (JSON, XML, fichier, …)

Vous allez aussi utiliser les Controller Play pour servir les pages HTML de votre site web. En effet, c’est Play votre serveur web (enfin Netty), qui devra répondre du contenu HTML lorsque vous l’interrogez sur une certaine URL (une « route »).

Dans cet article, on va utiliser la notion de « Template » dans Play. Un Template est un fichier à l’extension « .html », qui va contenir un mélange de HTML (+JavaScript éventuellement) et du Groovy (un peu comme Java) qui vous permet de réaliser des actions sur ce Template (boucles, conditions, …). Voici un petit exemple de Template:

<h1>Client ${client.name}</h1>
<ul>
    #{list items:client.accounts, as:'account' }
        <li>${account}</li>
    #{/list}
</ul>

Ce template devra être « interprété » pour au final cracher du HTML. C’est une étape que l’on appelle « exécution » d’un template. Celle-ci va être réalisée par vos Controller.

Notez que vous pouvez aussi utiliser les templates pour rendre autre chose que du HTML, du JSON par exemple. Un template permet de faciliter la génération de contenu complexe.

Conventions de nommage pour la liaison Controller / Template

Admettons que vous ayez l’instruction suivante:

public class Clients extends Controller {

    public static void index() {
        render();
    }
}

Votre Controller se trouve donc à l’emplacement /app/controllers/Clients.java. Lorsque vous allez appeler la méthode render(), Play va automatiquement aller cherche un Template à l’emplacement:

app/views/Clients/index.html

On verra plus loin que l’on peut bien sûr changer ce comportement par défaut et appeler un autre template.

Faire passer de la donnée du Controller vers le Template

Dans l’exemple plus haut, on a simplement appelé la méthode « render() ». Le template sera donc exécuté sans aucune donnée en entrée. Ce que l’on va plutôt chercher à faire, c’est passer des données au template (une liste de Clients par exemple) et laisse le template faire le rendu de cette liste de clients. Pour cela, vous avez deux méthodes.

Lire la suite

Play – Controller et les variantes de render() (JSON, XML, fichier, …)

Dans l’article précédent, on a vu comment gérer les paramètres « en entrée » d’un Controller, grâce au mapping manuel ou automatique effectué par Play:

Play – Controller et mapping des paramètres de requête HTTP (GET, POST, File, …)

Mais pour l’instant, vous n’avez rien en sortie. Le retour de votre requête HTTP est pour l’instant vide.

Pour pouvoir produire du contenu en sortie, vous allez utiliser une méthode de Play nommée « render(…) ». Celle-ci vous permettra de demander le rendu d’une View. Mais Play ne sert pas uniquement à servir des pages HTML. Vous pouvez bien sûr l’utiliser pour réaliser votre API, qui va retourner du JSON. Ou pour servir des fichiers, renvoyer du XML, …

Pour cela, Play expose d’autres méthodes, des variantes de la méthode render() qui seront plus spécialisées mais qui vous éviterons des lignes de code inutiles. La méthode renderJSON(…) par exemple va prendre en entrée un objet Java ou une List et s’occuper de la sérialiser en JSON.

Pourquoi render(…) et pas return?

Dans une méthode « classique », on a des paramètres en entrée et on renvoie (ou pas) des objets en sortie avec une instruction return. Dans le cas de Play, la méthode render (ou une de ses variantes) va en fait émettre un objet de type Result.

Quand je dis émettre, Play va en fait lancer une exception Java contenant cet objet Result. L’exception sera récupérée plus haut par l’initiateur de la requête et renvoyer le contenu en tant que réponse. Vous trouvez que lancer une exception c’est moche? Perso je trouve cela plutôt malin. Dans la pratique, c’est transparent.

Puisqu’un render va lancer une exception, cela signifie que tout ce qui se situe après la méthode render ne sera pas exécuté:

public static void show(Long id) {
    Client client = Client.findById(id);
    render(client);
    System.out.println("Code jamais exécuté");
}

Ce mécanisme permet d’être plus souple. Play va notamment pouvoir renvoyer correctement les types MIME des données renvoyées, ce qui aurait été difficile à faire avec une instruction return (trop limitée).

Renvoyer du texte simple (text plain)

La méthode renderText(…) permet de renvoyer du contenu texte directement dans la requête HTTP:

public static void countUnreadMessages() {
    Integer unreadMessages = MessagesBox.countUnreadMessages();
    renderText(unreadMessages);
}

Renvoyer du contenu au format JSON

Pour votre API publique, vous allez sûrement utiliser du JSON, qui est maintenant devenu le standard du web (notamment grâce au JSONP). Pour vous faciliter la vie, Play vous expose une méthode renderJSON(…) qui prend en paramètre un objet. Celui-ci sera automatiquement sérialisé grâce aux classes GSON (parser / stringifier de chez Google).:

public static void getUnreadMessages() {
    List<Message> unreadMessages = MessagesBox.unreadMessages();
    renderJSON(unreadMessages);
}

Renvoyer du contenu XML

De la même manière, Play vous permet de renvoyer directement du contenu XML (type MIME text/xml). Vous disposez donc d’une méthode renderXml(…) qui prend au choix:

  • Une chaîne au format XML
  • Un objet de type org.w3c.Document
  • Un objet quelconque qui sera sérialisé en XML par XStream
public static void countUnreadMessages() {
    Integer unreadMessages = MessagesBox.countUnreadMessages();
    renderXml("<unreadmessages>"+unreadMessages+"</unreadmessages>");
}

Renvoyer du contenu binaire (fichiers)

Play dispose d’une méthode renderBinary(…) vous permettant de renvoyer du binaire, par exemple un BLOB de votre BDD contenant une image. Comme ce contenu binaire peut être de n’importe quel type MIME, vous devrez le préciser vous-même à Play grâce à la méthode response.setContentTypeIfNotSet(…).

Lire la suite

Play – Controller et mapping des paramètres de requête HTTP (GET, POST, File, …)

Dans la conception d’une application web, un des aspects les plus importants est la logique métier, alias Business Logic. Cette logique métier est le lien entre la couche de présentation (vos View) et votre couche de donnée (vos Model).

Dans Play! Framework, la définition de votre logique métier se fait au sein des Controller, des classes Java (ou Scala) qui vont faire le lien entre une requête HTTP et votre Model. Bien sûr, aucune application ne se ressemble. On voit souvent les différences au niveau de l’interface utilisateur mais c’est surtout votre Business Logic qui va faire la différence avec les applications de vos concurrents.

Play va vous aider à écrire ces classes de Controller en vous fournissant des raccourcis ou des mappings automatique (par convention) qui vous éviterons de répéter inutilement certaines lignes de code.

Fonctionnement général d’un Controller Play

Un Controller dans le jargon Play est une classe qui se situe dans le package « controllers » et qui hérite de play.mvc.Controller. Par exemple:

package controllers;

import models.Client;
import play.mvc.Controller;

public class Clients extends Controller {

    public static void show(Long id) {
        Client client = Client.findById(id);
        render(client);
    }

    public static void delete(Long id) {
        Client client = Client.findById(id);
        client.delete();
    }

}

Chaque méthode « public static » est appelée une « action » dans Play. Ces actions renvoient la plupart du temps un type « void » (rien). En effet, pour indiquer à un Controller que l’on veut afficher une View (interface utilisateur), on va utiliser la méthode « render(…) » qui va exécuter le rendu d’un template.

Récupérer des paramètres de la requête HTTP

Ces « actions » peuvent avoir des paramètres qui seront automatiquement assignés par rapport à la requête HTTP entrante.

Mapping automatique

Imaginez que vous ayez la route suivante définie dans votre fichier de configuration routes:

GET    /clients/{id}             Clients.show

Les accolades signifient que l’id est la partie dynamique de l’URI. Si vous interrogez donc votre serveur avec l’URI /clients/1541, 1541 est votre id. Voici la méthode de votre Controller Clients qui va être appelée:

    public static void show(Long id) {
        Client client = Client.findById(id);
        render(client);
    }

Play va automatiquement faire le mapping entre le paramètre {id} de votre URI et le paramètre « id » de la méthode « show(Long id) ». Dans ce cas-là, Play va donc effectuer l’opération Client.findById(1541).

Play va aussi récupérer automatiquement les listes de paramètres que vous allez donner à votre requête. Par exemple pour:

/clients?id=1541&id=1654

Play va vous permettre de récupérer directement la liste des ids:

public static void show(Long[] id) {

Voici les types Java que vous pouvez utiliser directement dans la signature de vos paramètres, Play fera la conversion de type tout seul:

 

int, long, boolean, char, byte, float, double, Integer, Long, Boolean, Char, String, Byte, Float, Double.

Mapping manuel

Dans tous les cas, Play garde une Map<String, String[]> qui va contenir la liste des paramètres HTTP passés à la requête. En plus de cela, Play vous explose une méthode permettant de faire directement une conversion de type grâce à get(paramName, Class).

On aurait donc aussi pu écrire notre méthode show() comme ceci:

public static void show() {
	Long id = params.get("id", Long.class);
	Client client = Client.findById(id);
	render(client);
}

Bien sûr, cela fonctionne aussi avec les paramètres GET « classiques », comme avec l’URL /clients?id=1541 par exemple.

Le cas de l’upload de fichier

L’upload de fichier est assez commun dans une application web, ne serait-ce que pour que votre utilisateur envoie sa photo de profil. Lorsque Play reçoit un fichier issu d’un formulaire web « multipart/form-data », il va le stocker le temps de la requête dans un dossier temporaire et vous pourrez directement le récupérer dans votre Controller, en déclarant un paramètre de type « File »:

public static void create(String comment, File attachment) {
    String s3Key = S3.post(attachment);
    Document doc = new Document(comment, s3Key);
    doc.save();
    show(doc.id);
}

Si vous voulez conserver le fichier, n’oubliez pas de la copier dans votre logique métier car il sera détruit automatiquement par Play à la fin de la requête.

Lire la suite

Play – Fonctionnement général et cycle de vie d’une requête HTTP

Play! Framework est novateur dans le sens où il ne respecte pas l’architecture des projets J2EE « classiques », souvent pour simplifier la vie du développeur. En plus de cela, il repose sur certaines conventions / nomenclatures (un peu comme Rails) qu’il faut connaître. Le fonctionnement est simple et souvent logique, il ne vous faudra pas longtemps pour comprendre le fonctionnement global d’une application développée avec Play.

Séparation de la présentation et de la donnée avec MVC (Model-View-Controller)

Play sépare votre application en plusieurs couches, reprenant le pattern d’architecture MVC.

  • Le Model est un objet métier qui va être manipulé par votre application. Cela peut être un utilisateur, un rapport, un tweet, … Le Model ne doit pas être confondu avec ce que l’on nommé souvent un « Value Object » (VO). Un VO est une simple représentation de données (ensemble de propriétés), presque une ligne de votre base de données. Dans Play, un Model est un objet qui ajoute des méthodes spécifiques à votre objet métier. Par exemple dans un Model User, vous pourriez avoir une méthode qui calcule l’age de l’utilisateur, alors que vous avez comme donnée en BDD, sa date de naissance. De plus, le Model contient aussi la couche de « service » qui sert à accéder à la source de donnée. Pour récupérer un utilisateur, on va donc faire par exemple User.find(…), user.save(), user.delete(), … Grâce à JPA, la persistance se fait de manière transparente, vous n’aurez pas à vous en occuper
  • La View qui effectue le rendu du Model, le plus souvent sous forme d’une interface utilisateur. Dans une aplplication Play, la vue est le plus souvent rendue en HTML / XML ou JSON
  • Le Controller va prendre en charge les actions utilisateurs et va déclencher les modifications sur les Model. Le plus souvent, un Controller écoute une requête HTTP (une URL), extrait la donnée entrante (paramètres, headers), modifie le Model et demande le rendu d’une View (la page).

Dans une application Play, ces trois couches sont séparées dans des sous-dossiers de /app/. On retrouve donc dans ces dossiers:

  • /app/controllers : Une classe Java par Controller. Chaque méthode public static de ce Controller est une « action », c’est-à-dire un point d’entrée dans votre code Java qui sera appelé lorsqu’une requête HTTP sera reçue par votre serveur.
  • /app/models/ : Chaque Model est représenté par une classe Java, héritant le plus souvent de la classe « Model » de Play. Grâce à cet héritage, Play va vous offrir directement les méthodes de persistance de l’objet (save, delete, find, …). Vous y trouverez aussi des annotations JPA permettant de définir les contraintes de votre stockage comme les jointures / règles de validation
  • /app/views/ : Le dossier views va contenir les templates Play. Ces derniers sont des fichiers HTML avec du Groovy (similaire à Java) permettant de réaliser des traitements sur la données entrante (conditions / boucles, …)