我有一个MVC应用程序,其中业务层在出现错误时抛出异常。例如,当有人试图注册已在我们的数据库中注册的电子邮件地址时。对于特定的例外情况,我想重新渲染当前视图(用户填写的数据),并在页面顶部显示错误消息。
我没有将try / catch语句添加到所有控制器操作,而是创建了一个派生自HandleErrorAttribute的类,以捕获中心位置的所有异常。在这个类中,我通过返回一个新的ViewResult并复制ViewData:
来渲染当前视图public class CustomHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
filterContext.Result = ShowErrorMessage("Error occured", filterContext);
filterContext.ExceptionHandled = true;
base.OnException(filterContext);
}
private static ActionResult ShowErrorMessage(string message, ExceptionContext filterContext)
{
filterContext.Controller.ViewBag.ErrorMessage = message;
return new ViewResult { ViewData = filterContext.Controller.ViewData };
}
}
不幸的是,ViewData不包含完整的模型。例如,标记为已禁用的字段不会回发到控制器。因此,当我再次渲染视图时,某些字段将为空。
我在这里遗漏了什么吗?或者我应该朝不同的方向再次显示当前页面,并显示错误消息?
答案 0 :(得分:1)
你应该在处理这种情况时采取不同的方向。 MVC已经提供了一种通过ModelState向用户显示错误的更简单的方法。
我将提供一个示例,它可以100%假设您可能拥有的内容...因为问题中提供了关于控制器和视图当前外观的零代码。
例如:
在你的控制器中(我假设你在谈论Create ActionResult)。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Property1, Property2, Property3, EmailAddress")] Object myObject)
{
if(ModelState.IsValid)
{
if(db.TableName.Any(x => x.Email.Equals(myObject.EmailAddress, StringComparison.CurrentCultureIgnoreCase)
{
ModelState.AddModelError("EmailAddress", "This Email Already Exists!");
return View(myObject);
}
/* otherwise continue */
}
}
然后你说你想在视图的顶部显示该错误信息。
因此,在您的视图中,您有一行如下所示:
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
通常,第一个参数始终为true
,因为默认情况下您有这样的行:
@Html.ValidationMessageFor(model => model.EmailAddress, "", new { @class = "text-danger" })
位于相应的文本框下。因此,如果您想在页面顶部显示错误,请确保ValidationSummary
行位于表单顶部,并将true
更改为false
。
请告诉我这是否有帮助!
答案 1 :(得分:0)
您不应该使用控件流的例外!这是一种不好的做法。
对于您的特定用例,(当有人尝试使用已注册的电子邮件注册时),您可以创建自定义验证属性并使用MVC中提供的模型验证框架。
public class UniqueEmail : ValidationAttribute
{
public override string FormatErrorMessage(string name)
{
return $"This email ({name}) is already registered";
}
protected override ValidationResult IsValid(object objValue,ValidationContext context)
{
var email = objValue as string;
int userId= Int32.MinValue;
var idProperty = context.ObjectType.GetProperty("Id");
if (idProperty != null)
{
var idValue = idProperty.GetValue(context.ObjectInstance, null);
if (idValue != null)
{
userId = (int) idValue;
}
}
var userRepository = new UserRepository();
var e = userRepository.GetUser(email);
if (e!=null && userId!=e.Id)
{
return new ValidationResult(FormatErrorMessage(email));
}
return ValidationResult.Success;
}
}
在这个验证属性中,我读取了应用此属性的属性的值(在您的情况下,它将是电子邮件),查询数据库以查看我们是否存在该电子邮件的记录,如果是,我们将比较该用户记录的Id属性值与当前视图模型的Id属性值不同(这是为了处理用户记录的编辑)。
在上面的代码中,我使用的UserRepository
使用GetUser
方法来检查我们是否已在系统中记录了此电子邮件。只需更新该部分即可,但需要使用数据访问代码进行检查。
现在你要做的就是用这个属性装饰你的视图模型
public class CreateUserVm
{
public int Id { set;get;}
[Required(ErrorMessage = "Email is required")]
[UniqueEmail]
public string Email { set;get;}
public string Name { set;get; }
}
在您的HttpPost操作方法中,只需在保存数据前检查ModelState
是否有效
[HttpPost]
public ActionResult Create(CreateUserVm model)
{
if(ModelState.IsValid)
{
// all good. Save
return RedirectToAction("Index");
}
return View(model);
}
当模型验证失败时,它会在您的视图中呈现验证错误,假设您在视图中有验证帮助程序。
@using (Html.BeginForm("Create", "Home", FormMethod.Post))
{
@Html.ValidationSummary("", true)
@Html.TextBoxFor(f => f.Email)
@Html.ValidationMessageFor(model => model.Email)
@Html.HiddenFor(a=>a.Id)
<input type="submit" />
}