Si vamos a realizar un CRUD Restful con cara y ojos, indudablemente necesitaremos algunas validaciones; aunque muchas de ellas podríamos permitir que las hiciera Hibernate, hay otras que no, y, de cualquier forma, dado que en este desarrollo queremos incorporar explicaciones acerca de como trabajar con test (Junit5), he decidido empezar escribiendo una clase dedicada a validaciones, y, a continuación, hacer los test para asegurarnos que trabaja bien.
En cualquier caso, lo primero que tenemos que hacer, es iniciar el proyecto de Spring
Iniciando el proyecto
Si utilizamos STS, podemos indicar
O bien acudir a https://start.spring.io, que utilizaremos en alguna otra ocasión, en la pantalla que se nos ofrece, completaremos los datos que nos interesa para definir el package
y, tras darle NEXT, podremos indicar las dependencias que queremos incluir; en este momento, y, dado que también quiero mostrar como añadir las dependencias, escogeremos las mínimas… o casi
Ahora ya podemos seleccionar «Finish», con lo que nos generará el punto de partida para nuestro proyecto
De momento, no hemos pedido que se nos incluyera el entorno de Test, pero, dada la importancia que tiene, ha sido incluido de forma automática
Algunas cosas que necesitaremos
Puede que necesitemos una clase para guardar constantes, como por ejemplo, el formato de la fecha: para ello, crearé el package com.recursosformacion.lcs.util, y en él la clase
Constantes.java
package com.recursosformacion.lcs.util;
import java.time.format.DateTimeFormatter;
public class Constantes {
public final static String FORMATO_FECHA = "dd-MM-yyyy";
public final static DateTimeFormatter FORMATO_FECHA_EU = DateTimeFormatter.ofPattern(FORMATO_FECHA);
}
y ya iremos añadiendo lo que necesitemos.
La otra clase que podemos necesitar será una para comprobar errores, y, aunque la mayor parte de ellos, en la vida real los podríamos detectar de otra forma, por motivos de la demo, aquí escribiremos la clase Rutinas , en donde escribiremos las validaciones necesarias .
- isVacio – recibe una string y devuelve true si es nula o esta vacia
public static boolean isVacio(String prueba) {
return prueba == null || prueba.equalsIgnoreCase("");
}
- isEmailValido – Comprobamos si el formato recibido en una string es el de correo electrónico
private final static String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
public static boolean isEmailValido(String email) {
return !isVacio(email) && email.matches(EMAIL_PATTERN);
}
- nuevoSiNoVacio – Comprueba si el segundo parámetro es nulo o vacio, y, si es asi, devuelve el primero, si no, devuelve el segundo parámetro.
Su firma con generico seria- T nuevoSiNoVacio(T a, T b)
static public <T> T nuevoSiNoVacioG(T valorActual, T valorNuevo) {
if (Objects.nonNull(valorNuevo) ) {
return valorNuevo;
} else {
return valorActual;
}
}
- isEmptyOrNull – Devuelve True si la coleccion recibida es nula o vacia
public static boolean isEmptyOrNull(Collection<?> collection) {
return (collection == null || collection.isEmpty());
}
- cumpleDNI – Esta funcion verifica que el DNI cumple el siguiente formato: xx.xxx.xxx-L y la longitud correcta
private final static String DNI_PATTERN = "\\d{2}\\.\\d{3}\\.\\d{3}-[a-zA-Z]";
private final static String LETRA_DNI = "TRWAGMYFPDXBNJZSQVHLCKE";
private final static int LONGITUD_DNI = 12;
public static boolean cumpleDNI(String dni) {
if (dni == null) {
return false;
}
// si es un NIE se hacen las operaciones necesarias para poder calcular luego la
// letra correcta
if (dni.startsWith("X")) {
dni = dni.replaceFirst("X", "0");
} else if (dni.startsWith("Y")) {
dni = dni.replaceFirst("Y", "1");
} else if (dni.startsWith("Z")) {
dni = dni.replaceFirst("Z", "2");
}
if (dni.length() != LONGITUD_DNI) {
return false;
}
if (!dni.matches(DNI_PATTERN)) {
return false;
}
String dniNumerico = dni.substring(0, dni.length() - 2).replace(".", "");
int valorNumerico = Integer.parseInt(dniNumerico);
Character letraDNI = Character.toUpperCase(dni.charAt(dni.length() - 1));
if (LETRA_DNI.charAt(valorNumerico % 23) == letraDNI) {
return true;
} else {
return false;
}
}
- comparaFechas – Compara dos fechas en formato LocalDate, y devuelve -1. o 1 segun cual sea mayor o si son iguales. Si se produce algún error, devuelve 999(por ejemplo, alguna fecha es nula)
public static int comparaFechas(LocalDate fecha, LocalDate min) {
if (fecha != null && min != null) {
return fecha.compareTo(min);
}
return 999;
}
- isGreater – Compara si una fecha es superior a otra
- isGreaterOrEqual – fecha superior o igual a mínima
- isLess – una fecha inferior a mínima
- isLessOrEqual – fecha inferior o igual a minima
public static boolean isGreater(LocalDate fecha, LocalDate min) {
if (Rutinas.comparaFechas(fecha, min) == 1) {
return true;
}
return false;
}
public static boolean isGreaterOrEqual(LocalDate fecha, LocalDate min) {
if (Rutinas.comparaFechas(fecha, min) >= 0 ) {
return true;
}
return false;
}
public static boolean isLess(LocalDate fecha, LocalDate min) {
if (Rutinas.comparaFechas(fecha, min) == -1) {
return true;
}
return false;
}
public static boolean isLessOrEqual(LocalDate fecha, LocalDate min) {
if (Rutinas.comparaFechas(fecha, min) == -1 ||Rutinas.comparaFechas(fecha, min) == 0 ) {
return true;
}
return false;
}
- esFechaValida – Recibe una fecha en string y compara si es un formato de fecha valido, revisando dias y meses
public static boolean esFechaValida(String fecha) {
if (isVacio(fecha)) {
return false;
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
Optional<LocalDate> date = Optional.empty();
try {
date = Optional.of(LocalDate.parse(fecha, formatter));
if (date.isPresent()) {
return true;
}
} catch (DateTimeParseException e) {
}
System.out.println(fecha);
return false;
}
- esPasswordValida – Comprueba si la cadena recibida cumple con las normas de contraseña que hayamos establecido
private static final String PASSWORD_PATTERN = "((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})";
public static boolean esPasswordValida(String password) {
return !isVacio(password) && password.matches(PASSWORD_PATTERN);
}
En este punto, nuestro árbol seria
y el módulo lo podéis recoger de mi repositorio en GitHub o copiarlo de aquí
package com.recursosformacion.lcs.util;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
public class Rutinas {
private static final String PASSWORD_PATTERN = "((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})";
/**
* Patron para validar el email, evitando puntos finales antes de la @ y que
* solo contenga caracteres alfanumericos
*/
private final static String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
/**
* Permite verificar que un DNI cumple con el Patron XX.XXX.XXX-L
*/
private final static String DNI_PATTERN = "\\d{2}\\.\\d{3}\\.\\d{3}-[a-zA-Z]";
/**
* Letras con las cuales se comprobara la validez del DNI
*/
private final static String LETRA_DNI = "TRWAGMYFPDXBNJZSQVHLCKE";
/**
* Longitud que debe tener todo DNI pasado a la aplicacion.
*/
private final static int LONGITUD_DNI = 12;
/**
* *******************************************************************************
* Devuelve true si la string pasada es nula o vacia
*
* @param email String a comprobar
* @return true, en caso que el formato sea valido
* @date: Octubre 2023
* @author: Miguel Garcia
*/
public static boolean isVacio(String prueba) {
return prueba == null || prueba.equalsIgnoreCase("");
}
/**
* *******************************************************************************
* Comprueba si el email tiene un formato que pueda ser valido.
*
* @param email String a comprobar
* @return true, en caso que el formato sea valido
* @date: Octubre 2023
* @author: Miguel Garcia
*
**************************************************************************************/
public static boolean isEmailValido(String email) {
return !isVacio(email) && email.matches(EMAIL_PATTERN);
}
/**
* *******************************************************************************
* Devuelve el valorNuevo si este no es nullo o vacio, si no, devuelve
* valorActual
*
* @param valorActual Valor que tiene el campo en la actualidad
* @param valorNuevo Valor que llega para modificar el valor actual
* @return true, en caso que el formato sea valido
* @date: Octubre 2023
* @author: Miguel Garcia
*/
static public String nuevoSiNoVacio(String valorActual, String valorNuevo) {
if (Objects.nonNull(valorNuevo) && !"".equalsIgnoreCase(valorNuevo)) {
return valorNuevo;
} else {
return valorActual;
}
}
static public <T> T nuevoSiNoVacioG(T valorActual, T valorNuevo) {
if (Objects.nonNull(valorNuevo) ) {
return valorNuevo;
} else {
return valorActual;
}
}
static public int nuevoSiNoVacio(int valorActual, int valorNuevo) {
if (Objects.nonNull(valorNuevo)) {
return valorNuevo;
} else {
return valorActual;
}
}
static public LocalDate nuevoSiNoVacio(LocalDate valorActual, LocalDate valorNuevo) {
if (Objects.nonNull(valorNuevo)) {
return valorNuevo;
} else {
return valorActual;
}
}
static public Long nuevoSiNoVacio(long valorActual, long valorNuevo) {
if (Objects.nonNull(valorNuevo)) {
return valorNuevo;
} else {
return valorActual;
}
}
static public Long nuevoSiNoVacio(Long valorActual, Long valorNuevo) {
if (Objects.nonNull(valorNuevo)) {
return valorNuevo;
} else {
return valorActual;
}
}
static public Object nuevoSiNoVacio(Object valorActual, Object valorNuevo) {
if (Objects.nonNull(valorNuevo)) {
return valorNuevo;
} else {
return valorActual;
}
}
/**
* **********************************************************************************
* Devuelve True si la coleccion recibida es nula o vacia
*
* @param collection
* @return
*/
public static boolean isEmptyOrNull(Collection<?> collection) {
return (collection == null || collection.isEmpty());
}
/**
* ***********************************************************************************
* Esta funcion verifica que el DNI cumple el siguiente formato: xx.xxx.xxx-L y
* la longitud correcta
*
* @param dni String con DNI a ser validado
* @return true, si el DNI cumple el estandar nacional.
* @date: Octubre 2023
* @author: Miguel Garcia
*
**************************************************************************************/
public static boolean cumpleDNI(String dni) {
if (dni == null) {
return false;
}
// si es un NIE se hacen las operaciones necesarias para poder calcular luego la
// letra correcta
if (dni.startsWith("X")) {
dni = dni.replaceFirst("X", "0");
} else if (dni.startsWith("Y")) {
dni = dni.replaceFirst("Y", "1");
} else if (dni.startsWith("Z")) {
dni = dni.replaceFirst("Z", "2");
}
if (dni.length() != LONGITUD_DNI) {
return false;
}
if (!dni.matches(DNI_PATTERN)) {
return false;
}
String dniNumerico = dni.substring(0, dni.length() - 2).replace(".", "");
int valorNumerico = Integer.parseInt(dniNumerico);
Character letraDNI = Character.toUpperCase(dni.charAt(dni.length() - 1));
if (LETRA_DNI.charAt(valorNumerico % 23) == letraDNI) {
return true;
} else {
return false;
}
}
/**********************************************************************************
* Fechas
***********************************************************************************/
/**
* **********************************************************************
* Compara dos fechas
*
* @param fecha Fecha a comprobar uno
* @param min Fecha comparacion
* @return -1, 0 +1
* @date: Octubre 2023
* @author: Miguel Garcia
*/
public static int comparaFechas(LocalDate fecha, LocalDate min) {
if (fecha != null && min != null) {
return fecha.compareTo(min);
}
return 999;
}
/**
* *******************************************************************************
* Valida fecha superior a minima
*
* @param fecha Fecha a comprobar uno
* @param min Fecha comparacion
* @return True
* @date: Octubre 2023
* @author: Miguel Garcia
*/
public static boolean isGreater(LocalDate fecha, LocalDate min) {
if (Rutinas.comparaFechas(fecha, min) == 1) {
return true;
}
return false;
}
/**
* *******************************************************************************
* Valida fecha superior o igual a minima
*
* @param fecha Fecha a comprobar uno
* @param min Fecha comparacion
* @return True
* @date: Octubre 2023
* @author: Miguel Garcia
*/
public static boolean isGreaterOrEqual(LocalDate fecha, LocalDate min) {
if (Rutinas.comparaFechas(fecha, min) == 1 || Rutinas.comparaFechas(fecha, min) == 0) {
return true;
}
return false;
}
/**
* ****************************************************************************
* Valida una fecha inferior a minima
*
* @param fecha Fecha a comprobar
* @param min Fecha comparacion
* @return True
* @date: Octubre 2023
* @author: Miguel Garcia
*/
public static boolean isLess(LocalDate fecha, LocalDate min) {
if (Rutinas.comparaFechas(fecha, min) == -1) {
return true;
}
return false;
}
/**
* ****************************************************************************
* Valida una fecha inferior o igual a minima
*
* @param fecha Fecha a comprobar
* @param min Fecha comparacion
* @return True
* @date: Octubre 2023
* @author: Miguel Garcia
*/
public static boolean isLessOrEqual(LocalDate fecha, LocalDate min) {
if (Rutinas.comparaFechas(fecha, min) == -1 ||Rutinas.comparaFechas(fecha, min) == 0 ) {
return true;
}
return false;
}
/**
* *******************************************************************************
* esFechaValida Recibe una string con formato fecha dd/mm/aaaa y comprueba el
* formato
*
* @param fecha
* @return
* @date: Octubre 2023
* @author: Miguel Garcia
*/
public static boolean esFechaValida(String fecha) {
if (isVacio(fecha)) {
return false;
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
Optional<LocalDate> date = Optional.empty();
try {
date = Optional.of(LocalDate.parse(fecha, formatter));
if (date.isPresent()) {
return true;
}
} catch (DateTimeParseException e) {
}
System.out.println(fecha);
return false;
}
/**
* Comprueba que la cadena recibida cumpla con las normas de contraseña
*
* @param password string con la contraseña introducida
* @return true si cumple con las especificaciones
*/
public static boolean esPasswordValida(String password) {
return !isVacio(password) && password.matches(PASSWORD_PATTERN);
}
}
Quiero destacar que este módulo no especula acerca de si algo esta bien o mal, se limita a dar resultados acerca de si la pregunta es cierta o falsa, y, es el programa que llama que deberá decidir acerca de la exactitud. Como veis, se trata de preguntar si el dato recibido tiene valor, o es null, y devuelve un True o un False
Conclusión
Todo este desarrollo lo teneis explicado con mas detalle en youTube, y, aunque es conveniente que intentéis escribirlo TODO vosotros, si queréis renunciar a ello, lo teneis tambien en GitHub
La explicación en YouTube:
Este desarrollo esta hecho para disponer de un fuente para explicar otros temas, tal y como se indica en Visión de conjunto con Spring
Relacionado
Descubre más desde Recursos para formacion
Suscríbete y recibe las últimas entradas en tu correo electrónico.