Validación de campos obligatorios - eliizquierdo/material_programacion_avanzada2026 GitHub Wiki
📦 Proyecto:
mini-crud2— ⬇️ Descargar ZIP · ☁️ Abrir en Codespace
En la página anterior (Validación de unicidad de código) el Servlet controlaba que el código no estuviera repetido. Pero esa validación supone que los datos llegan bien: si el código llega vacío o con texto, Integer.parseInt() lanza una NumberFormatException antes de llegar a consultar el DAO, y el usuario ve un error 500 en lugar de un mensaje claro.
En esta página completamos el método agregar() para que el Servlet controle, en este orden:
- Que el código y el nombre no lleguen vacíos.
- Que el código sea un número entero mayor que 0.
- Que el código no esté repetido (la validación que ya teníamos).
Si alguna falla, el Servlet vuelve al formulario con un mensaje de error y conservando lo que el usuario escribió. Si todas pasan, guarda y aplica el patrón PRG.
Luego de los cambios que haremos, cuando se envíe el formulario con un campo vacío el error se mostrará de la siguiente manera:
Antes de validar es necesario saber qué recibe el Servlet. request.getParameter() siempre devuelve un String, aunque el campo del formulario sea type="number":
| Situación en el formulario |
getParameter("codigo") devuelve |
|---|---|
El campo tiene el valor 5
|
"5" |
| El campo se envía vacío |
"" (texto vacío) |
| El campo tiene solo espacios | " " |
El campo no existe o el name está mal escrito |
null |
Dos consecuencias importantes:
-
Un campo vacío no llega como
null, llega como"". Por eso no alcanza con preguntarcodigoStr == null: también hay que preguntar si está vacío con.trim().isEmpty(). El.trim()quita los espacios, así que un nombre escrito solo con espacios también se detecta como vacío. -
Un
intnunca puede sernull. Es un tipo primitivo, siempre guarda un número. Por eso la validación de vacío se hace sobre elString(codigoStr) antes de convertirlo aint. Escribirif (codigo == null)con unintes un error de compilación.
💡 El operador
||(O lógico). En la condicióncodigoStr == null || codigoStr.trim().isEmpty(), si la primera parte es verdadera Java no evalúa la segunda (evaluación en cortocircuito). Gracias a eso no se produce unaNullPointerExceptional llamar a.trim()sobre unnull. El símbolo|se escribe con AltGr + 1.
Igual que en la validación de unicidad, el cambio se hace en el método agregar(), que necesita throws ServletException para poder hacer forward.
La idea es usar una variable error que empieza en null. Cada validación que falla le asigna un mensaje. Al final, si error sigue en null, los datos son válidos.
private void agregar(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException { // ServletException se necesita para el forward
String codigoStr = request.getParameter("codigo");
String nombre = request.getParameter("nombre");
String error = null;
int codigo = 0;
// 1. Campos obligatorios (se validan sobre el String, antes de convertir)
if (codigoStr == null || codigoStr.trim().isEmpty()) {
error = "El código es obligatorio.";
} else if (nombre == null || nombre.trim().isEmpty()) {
error = "El nombre es obligatorio.";
} else {
// 2. Formato y rango del código
try {
codigo = Integer.parseInt(codigoStr.trim());
if (codigo <= 0) {
error = "El código debe ser un número mayor que 0.";
}
} catch (NumberFormatException e) {
error = "El código debe ser un número entero.";
}
}
// 3. Unicidad: solo tiene sentido si el código ya es un número válido
if (error == null && dao.obtenerXCodigo(codigo) != null) {
error = "El código " + codigo + " ya está registrado. Ingrese uno diferente.";
}
// Si hubo algún error: volver al formulario con FORWARD
if (error != null) {
request.setAttribute("error", error);
request.getRequestDispatcher("/WEB-INF/vista/persona-form.jsp").forward(request, response);
return; // Detener la ejecución
}
// 4. Datos válidos: agregar y aplicar PRG
dao.agregar(new PersonaVO(codigo, nombre.trim()));
response.sendRedirect(request.getContextPath() + "/persona");
}-
El orden de las validaciones importa. Primero se controla que el código no esté vacío, después que sea un número y recién al final la unicidad. No tiene sentido buscar en el DAO un código que no se pudo convertir a
int. -
try / catchalrededor deparseInt(). Si el texto no es un número entero (por ejemplo"abc"o"3.5"),parseInt()lanzaNumberFormatException. En lugar de dejar que la excepción llegue al usuario como error 500, la capturamos y la transformamos en un mensaje. -
codigo <= 0y nocodigo != 0. Así se rechazan tanto el 0 como los números negativos. -
forward+return. Elforwarddevuelve el control al formulario sin cambiar la URL y manteniendo el request. Elreturnevita que el método siga ejecutándose y agregue la persona igual.
El bloque <c:if> que muestra el error es el mismo de la página de unicidad. Lo que cambia es cómo se precargan los campos.
En la validación de unicidad guardábamos los datos en un PersonaVO (request.setAttribute("persona", ...)) y el JSP los leía con ${requestScope.persona.codigo}. Ahora eso no funciona en todos los casos: si el código llega como "abc", no se puede crear un PersonaVO, porque su código es int.
La solución es leer directamente los parámetros originales con el objeto implícito param de EL. Como el Servlet hace forward, el request es el mismo y los parámetros siguen disponibles, tal cual los escribió el usuario:
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<title>Agregar Persona</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/styles.css" />
</head>
<body>
<div class="form-container">
<h1>Agregar Persona</h1>
<!-- Mensaje de error enviado por el Servlet -->
<c:if test="${requestScope.error != null}">
<div style="color: red; border: 1px solid red; padding: 10px;
margin-bottom: 15px; background-color: #ffe8e8;">
<strong>Error:</strong> ${requestScope.error}
</div>
</c:if>
<form action="${pageContext.request.contextPath}/persona" method="post">
<input type="hidden" name="action" value="agregar" />
<label>Código</label>
<input type="number" name="codigo" value="${param.codigo}" required />
<label>Nombre</label>
<input type="text" name="nombre" value="${param.nombre}" required />
<button class="btn" type="submit">Guardar</button>
<a class="btn btn-secondary" href="${pageContext.request.contextPath}/persona">Volver</a>
</form>
</div>
</body>
</html>
⚠️ Error frecuente: en la directivataglibel atributo esuri(con "i"), nourl. Si se escribeurl, Tomcat muestra un error 500 con el mensaje "Atributo incorrecto, [url]".
Al probar, puede parecer que la validación no funciona: se deja un campo vacío, se presiona Guardar y el error del Servlet no aparece. Lo que ocurre es que el atributo required hace que el navegador bloquee el envío y muestre su propio aviso ("Completa este campo"). El formulario nunca llega al Servlet.
Para comprobar que el Servlet valida correctamente, desactivamos la validación del navegador agregando novalidate al <form>:
<form action="${pageContext.request.contextPath}/persona" method="post" novalidate>Con novalidate el navegador envía el formulario aunque tenga campos vacíos, y es el Servlet quien decide. Los required pueden quedar en los input: mientras el <form> tenga novalidate, se ignoran.
Casos para probar:
| Código | Nombre | Mensaje esperado |
|---|---|---|
| (vacío) | Ana | El código es obligatorio. |
| 5 | (vacío) | El nombre es obligatorio. |
| 5 | (solo espacios) | El nombre es obligatorio. |
| 0 | Ana | El código debe ser un número mayor que 0. |
| -3 | Ana | El código debe ser un número mayor que 0. |
| (un código ya cargado) | Ana | El código … ya está registrado. |
| 7 | Ana | Se guarda y vuelve al listado (PRG). |
En todos los casos con error, el formulario debe volver con los valores que se habían escrito.
Algunos casos se pueden probar sin novalidate: el type="number" acepta el 0 y los negativos, y required acepta un nombre con solo espacios. Esos datos llegan al Servlet aunque el navegador valide.
🔐 ¿Por qué validar en el servidor si el navegador ya valida? La validación del navegador (
required,type="number") mejora la experiencia del usuario, pero se puede saltear con facilidad: basta con quitar elrequireddesde las herramientas del navegador (F12) o enviar el formulario desde otra herramienta. La única validación confiable es la del servidor. Por eso se usan las dos: la del navegador para avisar rápido y la del Servlet para garantizar que los datos sean correctos.
- Quitar el
requireddel campo código, eliminar temporalmente eltry / catchdel Servlet y enviar el código vacío. Observar el error 500 porNumberFormatExceptiony explicar por qué ocurre. - Aplicar la misma validación de campos obligatorios en el método
editar(). ¿Hace falta validar la unicidad del código al editar? ¿Por qué? (Pista: revisar el atributoreadonlydel campo código enpersona-editar.jsp).
validación del lado del servidor · getParameter · NumberFormatException · try/catch · trim() · isEmpty() · operador || · param · novalidate · required · forward · PRG
🧠 Autoevaluación — Mini-CRUD con MVC
← Validación de unicidad de código · 🏠 Inicio · Comparativa persona-form.jsp →
