Vous n'êtes pas connecté. Connexion
|
|
Création d'un blog avec Web Services + client AjaxDe $1Table des matières
IntroductionDans ce TP nous allons partir d'un projet existant, le faire tourner, et l'améliorer. Il s'agit d'un petit projet simulant un blog. Nous allons commencer par créer le projet, créer une classe entité représentant les articles du blog, ajouter des web services permettant de manipuler ces données via des URIs, et enfin développer un client en HTML + JavaScript utilisant la bibliothèque jQuery pour la manipulation du DOM et pour les requêtes Ajax. Création du projet, d'une classe entité Article, et d'un session bean façade
Nous devrons par la suite certainement améliorer/modifier/ajouter des choses dans cette classe (de nouveaux Web Services par exemple, accepter de nouveaux formats d'entrée ou de sortie, etc.). Création d'un petit client pour ajouter des données dans la base via un web service.Dans la page index.html, nous allons ajouter un formulaire permettant d'ajouter des articles. Mettez donc un formulaire de ce type : <form method="post" enctype="application/x-www-form-urlencoded" action="resources/articles"> <p> <label for="titre">Titre</label> <input type="text" name="titre"/> </p> <p> <label for="content">Texte</label> <textarea id="content" name="contenu"></textarea> </p> <p> <input name="soumettre" type="submit"> </p> </form> Ce formulaire va nous permettre de remplir la base de données sans trop se fatiguer. Il va falloir néanmoins ajouter un Web Service indiquant que l'on va travailler avec en entrée le type de données provenant d'un formulaire HTTP. Ce type n'est ni XML ni JSON... Notez que le formulaire fait un POST (action="POST"), ce qui dans le vocabulaire REST signifie "insère dans la collection des Articles" un nouvel article, puisque l'URI de la collection sur laquelle on pointe ("resources/articles") est bien "articles". Voir le cours.
Ajoutez donc dans le fichier .java de vos Web Services un truc qui ressemble à cela : @POST @Consumes({MediaType.APPLICATION_FORM_URLENCODED}) public Response createFromFormulaire(@FormParam("titre") String titre, @FormParam("contenu") String contenu) { Article a = new Article(titre, contenu); super.create(a); // Create URI for Response UriBuilder b = uriInfo.getBaseUriBuilder(); URI u = null; try { u = new URI(b.path(ArticleFacadeREST.class).build() + "/" + a.getId()); System.out.println("Donnée créée avec comme URI : " + u); System.out.println("Dans create titre: " + a.getTitre() + " content: " + a.getContenu()); } catch (URISyntaxException ex) { Logger.getLogger(ArticleFacadeREST.class.getName()).log(Level.SEVERE, null, ex); } return Response.created(u).build(); } Rajoutez également comme attribut de cette classe une variable de type UriInfo, annotée par @Context. Cette variable nous permettra dans le code de connaitre l'URI du web service invoqué. @Context UriInfo uriInfo; Dans le fichier ApplicationConfig.java, changez le chemin "de base" d'accès à vos Web Services (ligne 14) /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package service; import java.util.Set; import javax.ws.rs.core.Application; /** * * @author tirius */ // MICHEL BUFFA : changer la ligne ci-dessous ! @javax.ws.rs.ApplicationPath("resources") public class ApplicationConfig extends Application { @Override public Set<Class<?>> getClasses() { Set<Class<?>> resources = new java.util.HashSet<Class<?>>(); // following code can be used to customize Jersey 2.0 JSON provider: try { Class jsonProvider = Class.forName("org.glassfish.jersey.jackson.JacksonFeature"); // Class jsonProvider = Class.forName("org.glassfish.jersey.moxy.json.MoxyJsonFeature"); // Class jsonProvider = Class.forName("org.glassfish.jersey.jettison.JettisonFeature"); resources.add(jsonProvider); } catch (ClassNotFoundException ex) { java.util.logging.Logger.getLogger(getClass().getName()).log(java.util.logging.Level.SEVERE, null, ex); } addRestResourceClasses(resources); return resources; } /** * Do not modify addRestResourceClasses() method. * It is automatically re-generated by NetBeans REST support to populate * given list with all resources defined in the project. */ private void addRestResourceClasses(Set<Class<?>> resources) { resources.add(service.ArticleFacadeREST.class); } } Quelques remarques :
Exécutez votre application, utilisez le formulaire pour créer des articles, regardez à l'aide des devtools de votre navigateur la réponse envoyée chaque fois que l'on soumet le formulaire. Vous devriez avoir Location: suivi de l'URI de la donnée ajoutée.
Avant d'aller plus loin, nous allons tester rapidement les autres Web Services, maintenant que nous avons des données... Test de vos Web ServicesNous allons maintenant rapidement tester les Web Services. Pour cela il faut créer un second projet de test, de type Web application. Créez donc un second projet appelé testBlogWS, de type Web. Ne mettez rien dedans. Revenez sur le premier projet, celui du blog et faites clic droit/testRestFul Web Services. Une fenêtre de dialogue apparait, proposant de générer un client de test pour vos Web services. Ne prenez pas la première option, mais la seconde, qui vous demande de choisir parmis les projets, celui dans lequel le client de test va être généré. Choisissez le projet testBlogWS. Cliquez sur ok, attendez un peu... Normalement une fenêtre permettant de tester vos web services va apparaitre. Essayez de lister les données que vous avez créées dans la section précédente. Implémentation d'un blog à l'aide d'appels AJAXNous allons utiliser la librairie jQuery pour ajouter/modifier/supprimer/mettre à jour les données que nous avons créées. Je vous propose de récupérer dans son intégralité un TP que m'a rendu un étudiant de M2 Miage. Tout n'est pas parfait dans ce code (notamment, la manière dont les web services renvoient des réponses). Récupérez le projet, dézippez le, ouvrez-le dans netbeans, regardez le fichier de configuration de la base de données utilisée (glassfish-resources.xml), créez la base, lancez le projet, étudiez-le !
Comparez les classes Article.java, les web services que vous avez écrits dans votre projet, etc. avec ceux de ce projet. Regardez les fichiers Pour ceux qui ne connaissent pas jQuerySi vous ne connaissez pas Ajax et jQuery, allez faire un tour sur la page de ressources jQuery que j'ai réalisée :Ressources JQuery. Voir aussi pour de nombreux tutoriaux interactifs sur jQuery : http://www.w3schools.com/jquery/default.asp les sections "jquery selectors" et "ajax". Je fais un TP en M1 sur jQuery : TP jQuery, support de cours recommandé pour débuter : http://ejohn.org/apps/workshop/intro/ Upload de fichiers images avec le formulaire pour posterTesté avec NetBeans 8.0.2 et Glassfish 4.1. Glassfish 4.1.1 a des soucis de déploiement de source de données, il est buggé, donc évitez de l'utiliser pour le moment. Supports de coursJe vous propose dans cette partie d'implémenter l'upload d'images avec les posts dans le blog. Regarder dans les transparents du MOOC de M.Buffa les chapitres 4 (formulaires) et chapitre 6 (persistence, File API et XmlHttpRequest level 2) les techniques proposées par HTML5 :
Le MOOC HTML5 part 2, Week 3 contient également des informations sur le drag'n'drop et l'upload de fichiers. Ajouter un Web Service permettant la réception d'un formulaire avec des fichiers attachés
Entrez des données dans les champs du formulaire puis sélectionnez un ou plusieurs fichiers. Cliquez sur "Submit Form", regardez les traces dans la console de NetBeans, onglet GlassFish. Si vous ne vous êtes pas trompés eur le chemin de destination, les fichiers sont uploadés. Vérifiez que tout est ok en ouvrant l'URL de vos fichiers http://localhost:8080/fichiers.... Il faut que le projet contienne les fichiers jars nécessaires au support pour le MultiPartAttention, l'ajout de la fonctionalité permettant à Jersey (le framework de Web Services REST de JavaEE) de gérer le multipart demande à rajouter quelques fichiers jars dans votre projet, sinon les classes MultiPart et FormDataMultiPart ne seront pas reconnues, et aussi altérer légèrement le fichier de config de vos Web Services dans votre projet: Voir la vidéo que je vous ai préparée, qui montre comment refaire cette partie du projet, pas à pas...
Code source des différentes fichiersCode source du formulaire d'envoi (on le soumettra en ajax, voir le fichier form.js qui suit):
<!DOCTYPE html> <html lang="en"> <head> <title>Form with file selector + ajax / Xhr2</title> <link rel="stylesheet" href="form.css"/> <script src="form.js"></script> </head> <body> <form onsubmit="return sendForm();" id="myForm"> <fieldset> <legend>Poster un nouveau message dans le Blog</legend> <label for="titre">Titre</label> <input type="text" id="titre" required name="titre"> <br> <label for="contenu">Contenu</label> <input type="text" id="contenu" required name="contenu"> <br> <label for="fileSelector">Images à uploader</label> <input type="file" id="fileSelector" multiple> <br> Progression de l'upload des fichiers: <progress value=0 id="progress"></progress> <p> <button>Submit form</button> </fieldset> </form> </body> </html>
var myForm, fileSelector, progress; var url = "/BlogWS2015/resources/files/upload"; window.onload = function () { myForm = document.querySelector("#myForm"); fileSelector = document.querySelector("#fileSelector"); progress = document.querySelector("#progress"); } function sendForm() { console.log("in sendForm()"); // SEND THE FORM USING AJAX var myForm = document.querySelector("#myForm"); var fileSelector = document.querySelector("#fileSelector"); // On remplit un objet FormData pour envoyer le formulaire // (y compris les fichiers attachés) en multipart var data = new FormData(myForm); var files = fileSelector.files; for (var i = 0; i < files.length; i++) { var name = "file"; data.append(name, fileSelector.files[i]); } sendFormDataWithXhr2(url, data); return false; } function sendFormDataWithXhr2(url, data) { // ajax request var xhr = new XMLHttpRequest(); xhr.open('POST', url); // With FormData, // POST is mandatory xhr.onload = function () { console.log('Upload complete !'); }; xhr.onerror = function () { console.log("erreur lors de l'envoi"); } xhr.upload.onprogress = function (e) { progress.value = e.loaded; // number of bytes uploaded progress.max = e.total; // total number of bytes in the file }; // send the request xhr.send(data); } Code source du Web Service package service; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.glassfish.jersey.media.multipart.ContentDisposition; import org.glassfish.jersey.media.multipart.FormDataBodyPart; import org.glassfish.jersey.media.multipart.FormDataMultiPart; @Path("/files") public class JerseyFileUpload { //private static final String SERVER_UPLOAD_LOCATION_FOLDER = "/Users/mbuffa/Desktop/"; private static final String SERVER_UPLOAD_LOCATION_FOLDER = "/Applications/NetBeans/glassfish-4.1/glassfish/domains/domain1/docroot/uploadedImages/"; /** * Upload a File * @param form * @return */ @POST @Path("upload") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadFile(FormDataMultiPart form) { // Get all parts //List<BodyPart> parts = form.getBodyParts(); System.out.println("UPLOAD MULTIPART FORM"); // GETTING THE FORM FIELDS String titre = form.getField("titre").getValue(); String contenu = form.getField("contenu").getValue(); System.out.println("Titre = " + titre); System.out.println("Contenu = " + contenu); // GETTING THE FILES List<FormDataBodyPart> files = form.getFields("file"); System.out.println("J'ai récupéré et je sauvegarde les fichiers suivants : "); for (FormDataBodyPart filePart : files) { ContentDisposition headerOfFilePart = filePart.getContentDisposition(); String filePath = SERVER_UPLOAD_LOCATION_FOLDER + headerOfFilePart.getFileName(); InputStream fileInputStream = filePart.getValueAs(InputStream.class); System.out.println("Fichier : " + filePath); // Get the inputStream for the file and save it saveFile(fileInputStream, filePath); } String output = "Files saved to server location using FormDataMultiPart ! "; return Response.status(200).entity(output).build(); } // save uploaded file to a defined location on the server private void saveFile(InputStream uploadedInputStream, String serverLocation) { try { OutputStream outpuStream = null; int read = 0; byte[] bytes = new byte[1024]; outpuStream = new FileOutputStream(new File(serverLocation)); while ((read = uploadedInputStream.read(bytes)) != -1) { outpuStream.write(bytes, 0, read); } outpuStream.flush(); outpuStream.close(); uploadedInputStream.close(); } catch (IOException e) { System.out.println("Erreur dans la sauvegarde du fichier : " + serverLocation); } } } Et le fichier de config des Web Services package service; import java.util.Set; import javax.ws.rs.core.Application; import org.glassfish.jersey.media.multipart.MultiPartFeature; @javax.ws.rs.ApplicationPath("resources") public class ApplicationConfig extends Application { @Override public Set<Class<?>> getClasses() { Set<Class<?>> resources = new java.util.HashSet<>(); // ### LA LIGNE A AJOUTER !!!!! ### // -------------------------------- resources.add(MultiPartFeature.class); addRestResourceClasses(resources); return resources; } /** * NE PAS TOUCHER, GENERE AUTOMATIQUEMENT */ private void addRestResourceClasses(Set<Class<?>> resources) { resources.add(service.ArticleFacadeREST.class); resources.add(service.JerseyFileUpload.class); } } Travail à rendre pour la rentrée en JanvierFaire fonctionner le projet Blog avec les contraintes suivantes:
En binôme. Vous le rendrez le projet lors de la première séance du prochain cours que j'ai avec vous. |
Powered by MindTouch Deki Open Source Edition v.8.08 |