Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

1K
Vistas
Springboot: mejor manejo de mensajes de error

Estoy desarrollando una API con Spring Boot y actualmente estoy pensando en cómo manejar los mensajes de error de una manera fácilmente internacionalizable. Mis objetivos son los siguientes:

  1. Definir mensajes de error en archivos/paquetes de recursos
  2. Conecte la anotación de restricción con mensajes de error (p. ej., @Length ) de forma declarativa
  3. Los mensajes de error contienen marcadores de posición, como {min} , que se reemplazan por el valor correspondiente de la anotación, si está disponible, por ejemplo, @Length(min = 5, message = msg) daría como resultado algo como msg.replace("{min}", annotation.min()).replace("{max}", annotation.max()) .
  4. La ruta de la propiedad JSON también está disponible como marcador de posición y se inserta automáticamente en el mensaje de error cuando se produce un error de validación.
  5. Se prefiere una solución fuera de un controlador de errores, es decir, cuando las excepciones llegan al controlador de errores, ya contienen los mensajes de error deseados.
  6. Los mensajes de error de un paquete de recursos se registran automáticamente como constantes en Java.

Actualmente, personalicé el methodArgumentNotValidHandler de mi clase de controlador de errores para leer ObjectError s de e.getBindingResult().getAllErrors() y luego traté de extraer sus argumentos y códigos de error para decidir qué mensaje de error elegir de mi paquete de recursos y formatearlo en consecuencia. . Un boceto aproximado de mi código se ve de la siguiente manera:

Aporte:

 @Data @RequiredArgsConstructor public class RequestBody { @NotNull @NotBlank(message = ErrorConstants.NOT_BLANK) @Length(min = 5, max = 255, message = ErrorConstants.LENGTH_MIN_MAX) // LENGTH_MIN_MAX = validation.length.min-max private String greeting; }

Controlador de errores:

 @ResponseBody @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) ErrorMessage methodArgumentNotValidHandler(MethodArgumentNotValidException e) { ObjectError objectError = e.getBindingResult().getAllErrors().get(0); Object[] arguments = objectError.getArguments(); String messageCode = objectError.getDefaultMessage(); // eg, "validation.length.min-max" (key in resource bundle) ResourceBundle errMsgBundle = ResourceBundle.getBundle("errorMsg"); String message; if (objectError.getCode().equals("Length")) { String messageTemplate = errMsgBundle.getString(messageCode); message = String.format(messageTemplate, arguments[2], arguments[1]); } else { message = "Bad input, but I cannot tell you the problem because the programmer hasn't handled this yet. Sorry :'("; } return new ErrorMessage(message); }

Desafortunadamente, supongo que este enfoque no se puede mantener. En el controlador de errores, terminaré con un gran bloque if-else que tiene que probar varias situaciones diferentes (códigos de error, cantidad de argumentos, ...) y formatear los mensajes de error en consecuencia. Cambiar los mensajes de error posiblemente resulte en tener que cambiar el código (por ejemplo, el orden de los argumentos). Cada clave de propiedad debe estar presente como una constante en ErrorConstants , lo que me parece indeseable. Este código tampoco consulta el nombre o la ruta de la propiedad defectuosa, por ejemplo, "nombre".

Por eso,

  1. ¿Existe una solución que pueda satisfacer algunos o todos los requisitos mencionados anteriormente?
  2. ¿En qué lugar implementaría esto?
  3. ¿Hay al menos una mejor solución a la anterior?
  4. ¿Hay recetas o patrones en SpringBoot para manejar los errores de validación (definitivamente no soy el primero en pensar en esto)?
over 4 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

0

Puede ser que puedas tener

 @ExceptionHandler(ConstraintViolationException.class) protected ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException e, WebRequest request){ return Optional.ofNullable(e).map(ConstraintViolationException::getConstraintViolations).map(this::createException).orElseGet(this::generateGenericError); }

de la que puedes tener

 private ErrorBody createException(FieldError fieldError) { return ErrorBody.builder() .code(fieldError.getCode()) .message(fieldError.getDefaultMessage()) .field(fieldError.getField()) .value(fieldError.getRejectedValue()) .build(); }

Para que puedas usar

fieldError.getCode()

para mapear el valor clave del archivo de propiedades

over 4 years ago · Santiago Trujillo Denunciar

0

No soy un gran admirador de las anotaciones javax.validation. Principalmente porque los objetos cuyas clases están anotadas con estos no se pueden probar fácilmente.

Lo que recomiendo es registrar una implementación org.springframework.validation.Validator en su clase de controlador anotado @RestController de la siguiente manera:

 @InitBinder void initBinder(WebDataBinder binder) { if (binder.getTarget() == null) { return; } final var validator1 = // your validator1 instance //check if specific validator is eligible to validate request and its body if (validator1.supports(binget.getTarget().getClass()) { binder.setValidator(validator); } }

Después de dicho registro, Spring invoca dicho validador para hacer coincidir la solicitud y su cuerpo y lanza MethodArgumentNotValidException si el validador rechazó cualquiera de los campos de objeto dados.

En su controlador de excepciones anotado con @ControllerAdvice (tenga en cuenta que su alcance es solo para solicitudes http) puede manejar dicha excepción de la siguiente manera:

 @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseBody ErrorMessage handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { final var errors = e.getAllErrors() return new ErrorMessage(/* populate your error message based on given errors */); }

Si bien la implementación del validador podría haberse visto así:

 @Override public void validate(Object target, Errors errors) { final var credentials = (Credentials) target; //reject username field with given c000 code if it's null if (isNull(credentials.getUsername())) { errors.rejectValue("username", "c000", "username cannot be null"); return; } if (credentials.getUsername().trim().isEmpty()) { errors.rejectValue("username", "c001", "username cannot be empty"); return; } if (credentials.getUsername().length() > 256) { errors.rejectValue("username", "c002", "username cannot be longer than 256 characters"); return; } }

La ventaja de tal solución es:

  • puede realizar pruebas unitarias de dicho validador sin configurar el contexto de la aplicación, lo cual es rápido
  • cuando el validador rechaza el cuerpo solicitado (en realidad, usted lo proporciona;)) proporciona un código de error y un mensaje para que pueda asignarlo directamente a su respuesta de ErrorMessage sin profundizar más.
  • usted externaliza la lógica de validación a una clase dedicada, que corresponde a S en SOLID (principio de responsabilidad de objeto único), que es deseado por algunos desarrolladores

Si tiene alguna pregunta o duda, solo pregunte.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda