通常我在将数据提交到数据库之前在action方法中验证我的模型。
[HttpPost]
public ActionResult MyActionMethod(MyModelType model){
if (ModelState.IsValid){
//commit changes to database...
return View("SuccessView",model);
}
return View(model);
}
但是在一些非常罕见的情况下,我需要在提交模型时在业务层中执行一些额外的验证。如果发生验证错误,我想在业务层中引发异常并使用该异常返回带有验证错误的视图。
我正在寻找一种方法来实现它,而无需更改控制器中的任何代码。所以我正在寻找避免这种情况的方法:
[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);
}
有没有办法做到这一点?
我在考虑一个捕获ValidationExceptions的动作过滤器,并在常规[HandleError]
过滤器启动之前返回带有验证错误的合适视图。这样的事情可能吗?
编辑:我刚刚找到解决方案(见下文),但直到48小时后我才能将此标记为正确答案...
答案 0 :(得分:6)
我刚刚在ASP.NET MVC源代码中搜索了一下后找到了解决方案:
使用动作过滤器无法完成,因为在调用动作方法之前和之后调用它,但它实际上并不包含动作方法调用。
但是,可以使用自定义ActionMethodInvoker来完成:
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);
}
}
}
然后,在控制器上:
protected override IActionInvoker CreateActionInvoker()
{
return new CustomActionInvoker();
}
答案 1 :(得分:3)
您可以在动作过滤器中明确设置动作结果。但是,如果您使用ActionExecuting(filterContext.Result)来设置操作结果,那么将不会调用您的控制器代码。我认为,如果额外的验证逻辑与模型相关联,而不是ActionFilter,更好的解决方案是使用自定义模型绑定器。
希望有所帮助。
答案 2 :(得分:1)
为什么不定义静态BusinessValidator帮助程序并执行以下操作:
[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);
}