Grails - Manejo de excepciones en los controladores [validation errors]
Como regla general, si espera que el flujo natural del método devuelva un objeto, entonces si algo sucede lance una excepción. Por ejemplo si crateCard
no puede crear una tarjeta, debería lanzar una excepción ya que siempre debería poder crear una tarjeta con un token que ya ha sido validado.
Si usted anticipa el nulo ocasional y quiere manejarlo de cierta manera, vaya con el nulo. Por ejemplo findUser
podría devolver null
si no encuentra el usuario.
Solo lance excepciones cuando suceda algo excepcional, hay un ejemplo en internet en donde el programador está basando la lógica del programa con excepciones: si encuentras la instancia ve hacia acá, si no la encuentras ve hacia allá. Otro problema es que lanza excepciones si no encuentra una instancia lo cual no es una condición excepcional, lo que debería estar haciendo es regresar boolean para determinar si se encuentra o no la instancia en la bd.
Por ejemplo no se pudo escribir en la bd, o el disco duro está lleno. Estos errores no se muestran al usuario porque no tiene sentido informarle este tipo de errores. Simplemente se dejan pasar para que sean mostrados en una página de error 500.
Por ejemplo: no se pudo crear la tarjeta de crédito. Para poder notificar al usuario este tipo de errores hay tres alternativas:
Utilice este mecanismo de static constraints
para validar un command object/domain class, si necesita validaciones personalizadas invoque un servicio dentro del mismo código de validación.
También puede pasar el domain class a un servicio y agregar errores sobre su objeto errors
, si hubiera errores de validación retornar true
o false
.
Utilice domain.errors.reject()
para registrar errores globales y domain.errors.rejectValue()
para registrar errores sobre una propiedad específica. Sin embargo tenga cuidado si se trata de un domain class porque el objeto errors
es limpiado al terminar la transacción llamando así a save()
el cual limpia el objeto errors
. La manera de evitarlo es llamando el método discard()
enseguida de agregar los errores, para más detalles consulte el gist grails-validation-errors.
Esta alternativa ahorra muchos try/catch
en la aplicación y modela claramente la lógica que debe tomar un controlador cuando una operación retorne éxito o error.
Debe ser utilizada cuando la naturaleza del método indique que el retorno es parte de la lógica, por ejemplo findUser
debe retornar un User
si encuentra al usuario y null
si no lo encuentra.
Con grails podremos validar si el usuario es null
con:
User user = findUser()
if (!user) {
...
}
Hay que tener en cuenta que para métodos que modifican la base de datos, si se retorna un código de error habrá que hacer manualmente rollback
por lo que retornar códigos no es muy recomendable en métodos que modifican la base de datos.
En conclusión, retorne códigos de error cuando se haga operaciones de lectura y cuando la naturaleza del método indique claramente que podría retornar éxito/error.
params.X
->Se pueden obtener todos los valores que estaban en el request
params.controller
->Controlador que lanzó la excepción
params.action
->Acción que lanzó la excepción
def saveTC() {
def tokenId = params.tid
def membresia = Membresia.findById(params.mid)
conektaService.createCard(membresia.conektaCustomerId, tokenId) // throws ConektaRuntimeException
flash.success = "Se creo correctamente la tarjeta de crédito"
redirect(action: "listTC", params: [id: params.mid])
}
def catchConektaRuntimeException(ConektaRuntimeException ex) {
def membresia = Membresia.findById(params.mid)
flash.error = ex.getMessage()
render view: "/membresia/createTC", model: [membresiaInstance: membresia]
return
}
Ventajas
Esta arquitectura es más limpia porque saca de la lógica del controlador los try/catch
,
Desventajas
Se hace un poco complicado leer y entender a través de qué lugares se llega al exception handler. Además, la excepción debe incluir todos los datos para la renderizar el error dentro de la misma página, como el modelo o el lugar a dónde se tiene que redirigir.
Tal vez esta alternativa sea buena cuando se tengan controladores pequeños.
http://stackoverflow.com/questions/99683/which-and-why-do-you-prefer-exceptions-or-return-codes
http://stackoverflow.com/questions/729379/why-not-use-exceptions-as-regular-flow-of-control
https://talldavedotnet.files.wordpress.com/2013/06/bestpracticespresentation_20130605_web.pdf