Programación

1º DAM/DAW - Curso 2023-2024

User Tools

Site Tools


apuntes:web

This is an old revision of the document!


Creación de una aplicación web

Creación del proyecto

Estructura del proyecto

Figure 1: Estructura de un proyecto JSP

Dependencias

pom.xml
. . .
<dependencies>
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
  </dependency>
 
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.30</version>
  </dependency>
 
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.26</version>
  </dependency>
 
  <dependency>
    <groupId>org.jdbi</groupId>
    <artifactId>jdbi3-core</artifactId>
    <version>3.37.1</version>
  </dependency>
 
  <dependency>
    <groupId>org.jdbi</groupId>
    <artifactId>jdbi3-sqlobject</artifactId>
    <version>3.37.1</version>
  </dependency>
</dependencies>
. . .
<build>
  <finalName>ParqueNaturalApp</finalName>
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.2</version>
      </plugin>
 
      <plugin>
        <!--
        mvn tomcat7:deploy para desplegar
        mvn tomcat7:undeploy para replegar
        mvn tomcat7:redeploy para redesplegar
        -->
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
 
        <configuration>
          <url>http://localhost:8082/manager/text</url>
          <server>tomcat9</server>
          <path>/parque</path>
          <username>tomcat</username>
          <password>tomcat</password>
        </configuration>
      </plugin>
    </plugins>
  </pluginManagement>
</build>
. . .

Ficheros de configuración del proyecto

context.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Context path="/amazon"/>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
 
<web-app>
  <display-name>Amazon web application</display-name>
</web-app>

Base de datos

CREATE TABLE activities (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50) UNIQUE NOT NULL,
  description VARCHAR(250),
  datetime DATETIME,
  price FLOAT,
  picture VARCHAR(50)
);
 
CREATE TABLE users (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL DEFAULT 'no-name',
  username VARCHAR(50) UNIQUE NOT NULL,
  password VARCHAR(40) NOT NULL,
  ROLE VARCHAR(50) DEFAULT 'user'
);

Creación de una página JSP

Creación de un Servlet

Crear un formulario para registrar información

Formulario JSP

<section class="py-5 container">
  <h1>Registrar nueva Actividad</h1>
  <h1>Modificar Actividad</h1>
  <form class="row g-3 needs-validation" method="post" enctype="multipart/form-data" id="edit-form">
    <div class="mb-3">
      <label for="name" class="form-label">Nombre</label>
      <input type="text" name="name" class="form-control" id="name" placeholder="Ir a caminar"
    </div>
    <div class="mb-3">
      <label for="description" class="form-label">Descripción</label>
      <textarea rows="4" name="description" cols="50" class="form-control" id="description" placeholder="Descripción de la actividad"></textarea>
    </div>
    <div class="col-md-4">
      <label for="date" class="form-label">Fecha</label>
      <input type="date" name="date" class="form-control" id="date" placeholder="dd/mm/yyyy"
    </div>
    <div class="col-md-4">
      <label for="price" class="form-label">Precio</label>
      <input type="text" name="price" class="form-control" id="price" placeholder="15,00"
    </div>
    <div class="col-md-4">
      <label for="picture" class="form-label">Foto</label>
      <input type="file" name="picture" class="form-control" id="picture">
    </div>
    <div class="col-12">
      <input type="submit" value="Enviar" id="edit-button"/>
    </div>
    <input type="hidden" name="id" value="<%= id %>"/>
  </form>
  <br/>
  <div id="result"></div>
</section>

Servlet para procesar la información

@WebServlet("/edit-activity")
@MultipartConfig
public class EditActivity extends HttpServlet {
 
  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html");
    response.setCharacterEncoding("UTF-8");
 
    HttpSession currentSession = request.getSession();
    if (currentSession.getAttribute("role") != null) {
      if (!currentSession.getAttribute("role").equals("admin")) {
        response.sendRedirect("/parque");
      }
    }
 
    try {
      if (hasValidationErrors(request, response))
        return;
 
      int id = 0;
      if (request.getParameter("id") != null) {
        id = Integer.parseInt(request.getParameter("id"));
      }
 
      String name = request.getParameter("name");
      String description = request.getParameter("description");
      Date date = DateUtils.parse(request.getParameter("date"));
      float price = CurrencyUtils.parse(request.getParameter("price"));
      Part picturePart = request.getPart("picture");
 
      // Guardar la imagen en disco
      String imagePath = request.getServletContext().getInitParameter("image-path");
      String filename = null;
      if (picturePart.getSize() == 0) {
        filename = "no-image.jpg";
      } else {
        filename = UUID.randomUUID() + ".jpg";
        InputStream fileStream = picturePart.getInputStream();
        Files.copy(fileStream, Path.of(imagePath + File.separator + filename));
      }
 
      Database.connect();
      final String finalFilename = filename;
      if (id == 0) {
        int affectedRows = Database.jdbi.withExtension(ActivityDao.class,
           dao -> dao.addActivity(name, description, date, price, finalFilename));
        sendMessage("Actividad registrada correctamente", response);
      } else {
        final int finalId = id;
        int affectedRows = Database.jdbi.withExtension(ActivityDao.class,
          dao -> dao.updateActivity(name, description, date, price, finalFilename, finalId));
        sendMessage("Actividad modificada correctamente", response);
      }
    } catch (ParseException pe) {
      pe.printStackTrace();
      sendError("El formato de fecha o precio no es correcto", response);
    } catch (ClassNotFoundException cnfe) {
      cnfe.printStackTrace();
      sendError("Internal Server Error", response);
    } catch (SQLException sqle) {
      sqle.printStackTrace();
      sendError("Error conectando con la base de datos", response);
    }
  }
 
  private boolean hasValidationErrors(HttpServletRequest request, HttpServletResponse response) throws IOException {
    boolean hasErrors = false;
 
    if (request.getParameter("name") == null) {
      sendError("El nombre es un campo obligatorio", response);
      hasErrors = true;
    }
    try {
      DateUtils.parse(request.getParameter("date"));
    } catch (ParseException pe) {
      sendError("Formato de fecha no válido", response);
      hasErrors = true;
    }
    try {
      float priceValue = CurrencyUtils.parse(request.getParameter("price"));
    } catch (ParseException pe) {
      sendError("Formato de precio no válido", response);
      hasErrors = true;
    }
 
    return hasErrors;
  }
}

Envío asíncrono del formulario

<script>
  $(document).ready(function () {
    $("#edit-button").click(function (event) {
      event.preventDefault();
      const form = $("#edit-form")[0];
      const data = new FormData(form);
 
      $("#edit-button").prop("disabled", true);
        $.ajax({
          type: "POST",
          enctype: "multipart/form-data",
          url: "edit-activity",
          data: data,
          processData: false,
          contentType: false,
          cache: false,
          timeout: 600000,
          success: function (data) {
            $("#result").html(data);
            $("#edit-button").prop("disabled", false);
          },
          error: function (error) {
            $("#result").html(error.responseText);
            $("#edit-button").prop("disabled", false);
          }
       });
    });
  });
</script>

Soporte para envío de imágenes en el formulario

web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app>
  <display-name>Parques Naturales</display-name>
 
  <context-param>
    <description>Ruta donde guardar las imagenes</description>
    <param-name>image-path</param-name>
    <param-value>/home/santi/apache-tomcat-9.0.86/webapps/parque_pictures</param-value>
  </context-param>
</web-app>
. . .
// Recoge la imagen adjuntada en el formulario a través de un 'input' de tipo 'file' con el nombre 'picture'
Part picturePart = request.getPart("picture");
 
// Ruta base para las imágenes. Coge el valor que hay fijado en 'web.xml' con ese nombre
String imagePath = request.getServletContext().getInitParameter("image-path");
String filename = null;
if (picturePart.getSize() == 0) {
  // El usuario no adjunta foto al formulario. Se le asigna una que ya tenemos preparada por defecto
  filename = "no-image.jpg";
} else {
  // El usuario adjunta foto al formulario. Generamos nombre aleatorio y la movemos a la ruta destino que hemos configurado en 'web.xml'
  filename = UUID.randomUUID() + ".jpg";
  InputStream fileStream = picturePart.getInputStream();
  Files.copy(fileStream, Path.of(imagePath + File.separator + filename));
}
. . .

Crear una página JSP para mostrar información

JSTL: JavaServer Pages Standard Tag Library

<dependencies>
  . . .
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
  </dependency>
  . . .
</dependencies>
. . .
<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %>
. . .
<%
  . . .
  pageContext.setAttribute("nombreAtributo", "valorAtributo");
  . . .
%>
. . .
<c:out value="${nombreAtributo}"/>
. . .
. . .
<c:if test="${nombreAtributo=='valorAtributo'}">
  <c:out value="El atributo 'nombreAtributo' es igual a 'valorAtributo'"/>
</c:if>
. . .

Uso de sesiones. Login y área privada

Inicio de sesión. Login

login.jsp
. . .
<script type="text/javascript">
  $(document).ready(function() {
    $("form").on("submit", function(event) {
      event.preventDefault();
      const formValue = $(this).serialize();
      $.ajax("login", {
        type: "POST",
        data: formValue,
        statusCode: {
          200: function(response) {
            if (response === "ok") {
              window.location.href = "/parque";
            } else {
              $("#result").html(response);
            }
          }
        }
      });
    });
  });
</script>
. . .
login.jsp
<main class="form-signin w-100 m-auto">
  <form>
    <h1 class="h3 mb-3 fw-normal">Iniciar sesión</h1>
    <div class="form-floating">
      <input type="text" name="username" class="form-control" id="floatingInput" placeholder="Usuario">
      <label for="floatingInput">Usuario</label>
    </div>
    <div class="form-floating">
      <input type="password" name="password" class="form-control" id="floatingPassword" placeholder="Contraseña">
      <label for="floatingPassword">Contraseña</label>
    </div>
 
    <button class="btn btn-primary w-100 py-2" type="submit">Iniciar sesión</button>
  </form>
  <br/>
  <div id="result"></div>
</main>
. . .
LoginServlet.java
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
 
  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html");
    response.setCharacterEncoding("UTF-8");
 
    String username = request.getParameter("username");
    String password = request.getParameter("password");
 
    try {
      Database.connect();
      User user = Database.jdbi.withExtension(UserDao.class,
        dao -> dao.getUser(username, password));
      if (user != null) {
        HttpSession session = request.getSession();
        session.setAttribute("username", user.getUsername());
        session.setAttribute("role", user.getRole());
        response.getWriter().print("ok");
      } else {
        sendError("El usuario no existe", response);
      }
    } catch (ClassNotFoundException cnfe) {
      cnfe.printStackTrace();
      sendError("Internal Server Error", response);
    } catch (SQLException sqle) {
      sqle.printStackTrace();
      sendError("Error conectando con la base de datos", response);
    }
  }
}

Mantenimiento de la sesión


© 2023-2024 Santiago Faci

apuntes/web.1711889423.txt.gz · Last modified: 2024/03/31 12:50 by Santiago Faci