2011-05-14 12 views
6

Normalmente estoy validando mi modelo en el método de acción antes de enviar datos a la base de datos.¿Puedo devolver un resultado de acción de un filtro de acción?

[HttpPost] 
public ActionResult MyActionMethod(MyModelType model){ 
if (ModelState.IsValid){ 
    //commit changes to database... 
    return View("SuccessView",model); 
} 
return View(model); 
} 

embargo, en algunos casos muy raros que necesito para realizar una validación adicional en la capa de negocio, mientras que el modelo que se está cometiendo. Si se produce un error de validación, me gustaría plantear una excepción en la capa empresarial y usar esa excepción para devolver una vista con errores de validación.

Estoy buscando una forma de implementar esto sin alterar ningún código en mi controlador. Así que estoy buscando una forma de evitar algo así:

[HttpPost] 
public ActionResult MyActionMethod(MyModelType model){ 
if (ModelState.IsValid){ 
    try { 
    //commit changes to database... 
    } catch (ValidationException e){ 
     ModelState.AddModelError(...); 
     return View(model); 
    } 
    return View("SuccessView",model); 

} 
return View(model); 
} 

¿Hay alguna manera de hacerlo?

Estaba pensando en un filtro de acción que atrape ValidationExceptions y devuelva la vista adecuada con errores de validación antes de que el filtro [HandleError] comience a funcionar. ¿Es posible algo así?

Editar: acabo de encontrar la solución (véase más adelante), pero no voy a ser capaz de marcar esto como la respuesta correcta hasta 48 horas han pasado ...

Respuesta

6

solo he encontrado la solución después de buscar un poco en el código fuente de ASP.NET MVC:

Se puede No se realizará con un filtro de acción porque se llama antes y después de llamar al método de acción, pero en realidad no cierra la llamada al método de acción.

Sin embargo, se puede hacer con un ActionMethodInvoker personalizado:

public class CustomActionInvoker : ControllerActionInvoker 
     { 
      protected override ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, System.Collections.Generic.IDictionary<string, object> parameters) 
      { 
       try 
       { 
        //invoke the action method as usual 
        return base.InvokeActionMethod(controllerContext, actionDescriptor, parameters); 
       } 
       catch(ValidationException e) 
       { 
        //if some validation exception occurred (in my case in the business layer) 
        //mark the modelstate as not valid and run the same action method again 
        //so that it can return the proper view with validation errors. 
        controllerContext.Controller.ViewData.ModelState.AddModelError("",e.Message); 
        return base.InvokeActionMethod(controllerContext, actionDescriptor, parameters); 
       } 
      } 
    } 

Y luego, en el controlador:

protected override IActionInvoker CreateActionInvoker() 
{ 
    return new CustomActionInvoker(); 
} 
+1

Muy bien, ¡no sabía de este punto de extensión hasta ahora! – Oliver

3

Puede, evidentemente, establecer la acción resultado en filtro de acción. Pero si usa ActionExecuting (filterContext.Result) para establecer el resultado de la acción, entonces no se invocará el código de su controlador. Creo que en lugar de ActionFilter, si la lógica de validación adicional está vinculada con el modelo, una mejor solución sería usar un encuadernador de modelo personalizado.

Espero que ayude.

+0

El problema es que la lógica de validación adicional no es realmente ligado con el modelo . Las reglas comerciales que intento implementar están hechas a medida para una sola compañía que está usando mi sitio web SaaS. Quieren algunas reglas de validación adicionales que no deberían aplicarse a otros usuarios. Como esto es muy especial, quiero mantenerlo fuera de mi código normal tanto como sea posible. Poner la lógica de validación adicional en el modelo o el controlador es exactamente lo que estoy tratando de evitar. –

+0

@Adrian Tal vez pueda crear carpetas de modelos diferentes y registrarse según el cliente de su aplicación SaaS. De hecho, en el caso de los filtros de Acción, aún debe averiguar qué cliente debe aplicar la lógica personalizada. – kazimanzurrashid

1

¿Por qué no se define un ayudante BusinessValidator estática y hacer algo como:

[HttpPost] 
public ActionResult MyActionMethod(MyModelType model){ 
var businessErrors = null; 
if ((ModelState.IsValid) && (BusinessValidator<MyModelType>.IsValid(model, out businesErrors)){ 
    //commit changes to database... 
    return View("SuccessView",model); 
} 

if (businessErrors != null) 
{ 
// TODO: add errors to the modelstate 
} 

return View(model); 
} 
+0

Las reglas comerciales que intento implementar están hechas a medida para una sola compañía que está usando mi sitio web SaaS. Quieren algunas reglas de validación adicionales que no deberían aplicarse a otros usuarios. Como esto es muy especial, quiero mantenerlo fuera de mi código normal tanto como sea posible. Poner la lógica de validación adicional en el modelo o el controlador es exactamente lo que estoy tratando de evitar. –

Cuestiones relacionadas