En este tema vamos a aprender a centralizar la gestión de todas las rutas de nuestra web con un Front Controller, de manera que podamos servir tanto páginas HTML dinámicas como recursos estáticos (CSS, JS, imágenes).

Nuestro objetivo será:

  • Crear un Front Controller en Java con Jakarta EE.

  • Servir HTML, CSS y JS correctamente.

  • Preparar el proyecto para desplegarlo como ROOT.war en Tomcat, de modo que las rutas relativas funcionen sin problemas.

 miweb/
miweb/
├── src/
│   └── main/
│       ├── java/                  <-- paquetes con clases Servlet
│       │   └── es/cesguiro/servlets/web/
│       │       └── FrontController.java
│       └── webapp/
│           ├── index.jsp          <-- archivo JSP inicial (lo borramos)
│           ├── WEB-INF/
│           │   ├── web.xml        <-- descriptor web (opcional en Servlet 3+)
│           └── META-INF/          <-- opcional
│           ├── index.html         <-- HTML principal
│           ├── books.html         <-- página de libros
│           ├── css/               <-- css básico
│           │   └── style.css
│           └── js/                <-- js básico
│               └── script.js

Todos los HTML deben usar rutas relativas al contexto para CSS y JS, por ejemplo:
<link rel="stylesheet" href="css/style.css">
<script src="js/script.js"></script>

Un Front Controller es un patrón muy habitual en aplicaciones web: consiste en un único servlet que actúa como punto de entrada de toda la aplicación. Todas las peticiones HTTP pasan primero por él, y desde allí se decide qué lógica ejecutar o qué página devolver.

En nuestro proyecto, el FrontController:

  • Recibe todas las rutas (/*).

  • Decide si la petición es para un recurso estático (CSS, JS, imágenes) o para una ruta dinámica (páginas HTML servidas vía servlet).

  • Reenvía la solicitud a la página correspondiente usando RequestDispatcher.forward() o devuelve un error 404 si no encuentra la ruta.

package es.cesguiro.servlets.web;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.InputStream;

@WebServlet(name = "frontController", urlPatterns = "/*")
public class FrontController extends HttpServlet {

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String path = request.getPathInfo();
    if (path == null || path.equals("/") || path.isEmpty()) {
        path = "/index.html";
    }

    // Intentar servir un recurso estático (CSS, JS, imágenes)
    if (handleStaticResource(request, response, path)) {
        return;
    }

    // Rutas dinámicas
    switch (path) {
        case "/books":
            request.getRequestDispatcher("/books.html").forward(request, response);
            break;
        default:
            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Page not found: " + path);
            break;
    }
}

private boolean handleStaticResource(HttpServletRequest request, HttpServletResponse response, String path) throws IOException {
    try (InputStream inputStream = getServletContext().getResourceAsStream(path)) {
        if (inputStream != null) {
            String mimeType = getServletContext().getMimeType(path);
            if (mimeType == null) {
                mimeType = "application/octet-stream";
            }
            if (mimeType.startsWith("text/")) {
                response.setCharacterEncoding("UTF-8");
            }
            response.setContentType(mimeType);
            response.setStatus(HttpServletResponse.SC_OK);
            inputStream.transferTo(response.getOutputStream());
            return true;
        }
    }
    return false;
}


}

Este Front Controller permite servir HTML dinámico y recursos estáticos como CSS y JS desde webapp.

Tomcat tiene un servlet por defecto llamado DefaultServlet que sirve archivos estáticos desde webapp/.

Si usamos DefaultServlet, podemos acceder a CSS, JS o imágenes simplemente con rutas relativas:

<link rel=“stylesheet” href=“/css/style.css”>

Si creamos un Front Controller que captura todas las rutas con /*, este servlet intercepta todas las peticiones, incluidas las de recursos estáticos (CSS, JS, imágenes). Esto provoca que:

  • Los archivos estáticos ya no sean servidos automáticamente por DefaultServlet.

  • Las rutas relativas como /css/style.css dejan de funcionar.

Posibles soluciones

  • Servir recursos estáticos desde nuestro Front Controller
    • Como hemos hecho en el ejemplo de handleStaticResource().
    • Ventaja: todo pasa por un solo servlet.
    • Inconveniente: debemos gestionar MIME types y rutas manualmente.

  • Desplegar la web en un subcontexto, por ejemplo /app
    • La app queda accesible desde http://localhost:8080/app/.
    • El Front Controller solo captura rutas dentro de /app/*.
    • DefaultServlet sigue disponible para otras rutas, permitiendo servir CSS y JS sin problemas.

Para que las rutas relativas de CSS y JS funcionen sin prefijos extra (/web/), debemos desplegar el WAR como ROOT.war:

En Maven, dentro de pom.xml:

<finalName>ROOT</finalName>

  • Build del proyecto (mvn clean install).

  • Copiar target/ROOT.war a tomcat/webapps/.

  • Arrancar Docker: docker-compose up.

  • Acceder a la web: http://localhost:8080/

  • Ahora todas las rutas relativas funcionarán correctamente, incluyendo CSS y JS.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello World</title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>
    <h1>Hello World</h1>
</body>
</html>

  • clase/daw/si/1eval/tomcat2.txt
  • Última modificación: 2025/08/14 18:51
  • por cesguiro