返回当前页面并显示错误消息

时间:2017-10-26 14:45:39

标签: c# asp.net-mvc

我有一个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不包含完整的模型。例如,标记为已禁用的字段不会回发到控制器。因此,当我再次渲染视图时,某些字段将为空。

我在这里遗漏了什么吗?或者我应该朝不同的方向再次显示当前页面,并显示错误消息?

2 个答案:

答案 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"  />
}