Utiliser des formulaires avec Java pour l'environnement standard App Engine

Cette partie du tutoriel consacré à Bookshelf pour Java explique comment accéder aux données de formulaire à partir de servlets.

Ce tutoriel comporte plusieurs pages. Pour le suivre depuis le début et consulter les instructions relatives à la configuration, accédez à la page Application Bookshelf pour Java.

Exécuter l'application sur votre machine locale

Pour exécuter l'application localement :

  1. Dans le répertoire getting-started-java/bookshelf-standard/2-structured-data, saisissez la commande suivante pour démarrer un serveur Web local :

    mvn package appengine:run
  2. Dans votre navigateur Web, accédez à http://localhost:8080.

Déployer l'application dans l'environnement standard App Engine

Pour un déploiement dans l'environnement standard App Engine :

  1. Pour créer un index Datastore qui est téléchargé vers l'application Bookshelf, créez au moins un livre, puis cliquez sur Mes livres. Notez que la compilation de l'application à l'aide de la commande clean supprime l'index local. Faites-le donc sur la compilation de l'application que vous allez déployer.
  2. Dans le répertoire getting-started-java/bookshelf-standard/2-structured-data, entrez la commande suivante pour déployer l'application :
    mvn appengine:deploy -Dappengine.appId=[YOUR-PROJECT-ID] -Dappengine.version=[YOUR-VERSION]
    Remplacez [YOUR-PROJECT-ID] par votre ID de projet et [YOUR-VERSION] par votre version, par exemple 1 ou 2, ou par une autre valeur de chaîne que vous souhaitez utiliser.
  3. Dans votre navigateur Web, saisissez l'URL suivante :

    https://PROJECT_ID.REGION_ID.r.appspot.com

    Remplacez les éléments suivants :

Une fois que vous avez mis à jour votre application, vous pouvez redéployer la dernière version en saisissant la même commande que celle que vous avez utilisée pour déployer l'application pour la première fois, et en spécifiant le même ID de projet et la même version. Cela écrase l'application actuellement déployée. Si vous spécifiez une chaîne de version différente dans la ligne de commande mise à jour, le nouveau déploiement crée une nouvelle version de votre application et la définie comme la version actuellement diffusée.

Vous pouvez réduire les coûts en supprimant les versions inactives de votre application.

Pour supprimer une version d'application :

  1. Dans Cloud Console, accédez à la page Versions pour App Engine.

    Accéder à la page Versions

  2. Cochez la case correspondant à la version de l'application autre que celle par défaut que vous souhaitez supprimer.
  3. Cliquez sur Supprimer  pour supprimer la version de l'application.

Pour en savoir plus sur le nettoyage des ressources facturables, reportez-vous à la section Nettoyage de la dernière étape de ce tutoriel.

Traiter les envois des utilisateurs avec des formulaires

Le formulaire vous permet d'ajouter et de modifier des envois de livres au sein de l'application.

Image du formulaire ajouter/modifier

Lorsque votre navigateur demande /create, le servlet Google Kubernetes Engine charge le CreateBookServlet et appelle la méthode doGet. La requête est ensuite transmise au servlet /base.jsp, qui comprend le servlet /form.jsp. Après avoir envoyé le formulaire, la méthode doPost de CreateBookServlet est appelée.

Dans CreateBookServlet.java, la méthode doGet traite la requête de formulaire et définit les attributs utilisés par le fichier JSP :

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
    IOException {
  req.setAttribute("action", "Add");          // Part of the Header in form.jsp
  req.setAttribute("destination", "create");  // The urlPattern to invoke (this Servlet)
  req.setAttribute("page", "form");           // Tells base.jsp to include form.jsp
  req.getRequestDispatcher("/base.jsp").forward(req, resp);
}

Le fichier base.jsp sert de base aux pages HTML de l'application. Il commence par inclure la bibliothèque de tags standards JSP. Les tags principaux JSTL sont identifiés par des tags commençant par <c:. Ils fournissent une variable, un contrôle de flux, une gestion des URL et diverses compatibilité. Les fonctions JSTL sont identifiées par des tags commençant par <fn:. Ils fournissent des collections et des bibliothèques de manipulation de chaînes. Voici un récapitulatif des tags JSTL utilisés par l'application Bookshelf :

Tag JSTL Description
<c:if ... Évalue l'élément d'expression test. Si vrai, inclut ce contenu.
<c:choose> Tag conditionnel qui établit le contexte pour c:when et c:otherwise.
<c:when ... Inclut le contenu si test est vrai.
<c:otherwise> Si c:when n'est pas vrai, incluet ce contenu.
<c:import url="" /> Inclut le contenu de l'url dans la page.
<c:out value="" /> Évalue l'expression de value et affiche une version codée.
${fn:escapeXml()} Échappe les caractères spéciaux pour les pages Web.


<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<html lang="en">
  <head>
    <title>Bookshelf - Java on Google Cloud Platform</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
  </head>
  <body>
    <div class="navbar navbar-default">
      <div class="container">
        <div class="navbar-header">
          <div class="navbar-brand">Bookshelf</div>
        </div>
        <ul class="nav navbar-nav">
          <li><a href="/">Books</a></li>
          <c:if test="${isAuthConfigured}"><li><a href="/books/mine">My Books</a></li></c:if>
        </ul>
        <p class="navbar-text navbar-right">
          <c:choose>
          <c:when test="${not empty token}">
          <!-- using pageContext requires jsp-api artifact in pom.xml -->
          <a href="/logout">
            <c:if test="${not empty userImageUrl}">
              <img class="img-circle" src="${fn:escapeXml(userImageUrl)}" width="24">
            </c:if>
            ${fn:escapeXml(userEmail)}
          </a>
          </c:when>
          <c:when test="${isAuthConfigured}">
          <a href="/login">Login</a>
          </c:when>
          </c:choose>
        </p>
      </div>
    </div>
    <c:import url="/${page}.jsp" />
  </body>
</html>

Le formulaire HTML, créé à l'aide de JavaServer Pages, ressemble à un moteur de création de modèles Java, mais s'exécute comme un servlet. Le code JSP suivant spécifie que le formulaire comprend des champs de saisie de texte pour le titre, l'auteur, la date de publication, la description et l'image de couverture, ainsi qu'un bouton d'enregistrement :

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<div class="container">
  <h3>
    <c:out value="${action}" /> book
  </h3>

  <form method="POST" action="${destination}">

    <div class="form-group">
      <label for="title">Title</label>
      <input type="text" name="title" id="title" value="${fn:escapeXml(book.title)}" class="form-control" />
    </div>

    <div class="form-group">
      <label for="author">Author</label>
      <input type="text" name="author" id="author" value="${fn:escapeXml(book.author)}" class="form-control" />
    </div>

    <div class="form-group">
      <label for="publishedDate">Date Published</label>
      <input type="text" name="publishedDate" id="publishedDate" value="${fn:escapeXml(book.publishedDate)}" class="form-control" />
    </div>

    <div class="form-group">
      <label for="description">Description</label>
      <textarea name="description" id="description" class="form-control">${fn:escapeXml(book.description)}</textarea>
    </div>

    <div class="form-group ${isCloudStorageConfigured ? '' : 'hidden'}">
      <label for="image">Cover Image</label>
      <input type="file" name="file" id="file" class="form-control" />
    </div>

    <div class="form-group hidden">
      <label for="imageUrl">Cover Image URL</label>
      <input type="hidden" name="id" value="${book.id}" />
      <input type="text" name="imageUrl" id="imageUrl" value="${fn:escapeXml(book.imageUrl)}" class="form-control" />
    </div>

    <button type="submit" class="btn btn-success">Save</button>
  </form>
</div>

Envoi de formulaire

La méthode doPost crée un objet Book en lisant les paramètres du formulaire par les appels req.getParameter(), puis fournit l'objet Book à un nouvel objet BookDao qui ajoute la nouvelle entité livre à Datastore. Une fois le livre enregistré dans Datastore, la page est redirigée vers /read afin que vous puissiez voir ce qui vient d'être enregistré. L'accès à la session et la création de livre sont abordés plus loin :

@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
    IOException {
  Book book = new Book.Builder()
      .author(req.getParameter("author"))   // form parameter
      .description(req.getParameter("description"))
      .publishedDate(req.getParameter("publishedDate"))
      .title(req.getParameter("title"))
      .imageUrl(null)
      .build();

  BookDao dao = (BookDao) this.getServletContext().getAttribute("dao");
  try {
    Long id = dao.createBook(book);
    resp.sendRedirect("/read?id=" + id.toString());   // read what we just wrote
  } catch (Exception e) {
    throw new ServletException("Error creating book", e);
  }
}