控制器中的异常处理.净MVC)

本文关键字:MVC 异常处理 控制器 | 更新日期: 2023-09-27 18:17:49

当一个异常是由你自己的代码,从一个控制器的动作调用应该如何处理?我看到很多根本没有try-catch语句的最佳实践示例。例如,从存储库访问数据:

public ViewResult Index()
{
    IList<CustomModel> customModels = _customModelRepository.GetAll();
    return View(customModels);
}

显然这段代码可能抛出一个异常,如果调用是一个数据库,它不能访问,我们正在使用ORM,如实体框架为例。

然而,我所能看到的将会发生的是,异常将会弹出,并向用户显示一个令人讨厌的错误消息。

我知道HandleError属性,但我知道它主要用于在发生未处理的异常时将您重定向到错误页面。

当然,这段代码可以包装在try-catch中,但不能很好地分离,特别是如果您有更多的逻辑:

public ViewResult Index()
{
    if (ValidationCheck())
    {
        IList<CustomModel> customModels = new List<CustomModel>();
        try
        {
            customModels = _customModelRepository.GetAll();
        }
        catch (SqlException ex)
        {
            // Handle exception
        }
        if (CustomModelsAreValid(customModels))
            // Do something
        else
            // Do something else
    }
    return View();
}

以前我已经提取出所有可能抛出异常的代码,例如数据库调用到DataProvider类,该类处理错误并返回消息以向用户显示消息。

我想知道处理这个问题的最好方法是什么?我不希望总是返回到错误页面,因为有些异常不应该这样做。相反,应该以正常视图显示给用户的错误消息。我以前的方法是正确的还是有更好的解决方案?

控制器中的异常处理.净MVC)

为了显示更友好的消息,我做了三件事:

    利用全局异常处理程序。在MVC: Application_Error在Global.asax的情况下。在这里学习如何使用它:http://msdn.microsoft.com/en-us/library/24395wz3(v=vs.100).aspx
  1. 我将Exception子类化为UserFriendlyException。我在所有底层服务类中尽最大努力抛出这个UserFriendlyException,而不是一个普通的旧Exception。我总是尝试在这些自定义异常中放入有用户意义的消息。其主要目的是能够对Application_Error方法中的异常进行类型检查。对于UserFriendlyExceptions,我只使用我在服务中设置的用户友好消息,比如"嘿!91度不是一个有效的纬度值!"如果它是一个常规异常,那么它是我还没有处理过的情况,所以我显示一个更通用的错误消息,比如"哎呀,出错了!"我们会尽全力解决这个问题的!"
  2. 我还创建了一个ErrorController,负责呈现用户友好的视图或JSON。这个控制器的方法将从Application_Error方法中调用。
编辑:

我想我应该提一下ASP。NET Web API,因为它是密切相关的。因为Web API端点的使用者不一定是浏览器,所以我喜欢用稍微不同的方式处理错误。我仍然使用"FriendlyException"(上面的#2),但不是重定向到ErrorController,而是让所有端点返回某种包含Error属性的基类型。如果一个异常一直冒泡到Web API控制器,我要确保把那个错误放在API response的error属性中。这个错误消息要么是从API控制器所依赖的类中产生的友好消息,要么是异常类型不是FriendlyException的泛型消息。这样,消费客户端可以简单地检查API响应的Error属性是否为空。如果存在错误,则显示一条消息,如果没有,则照常进行。好处是,由于友好的消息概念,该消息对用户来说可能比一般的"Error!"消息更有意义。我在用Xamarin编写手机应用程序时使用了这个策略,我可以在我的web服务和我的iOS/Android应用程序之间共享我的c#类型。

你也可以为你的控制器重写OnException方法。

protected override void OnException(ExceptionContext filterContext)
{
    if (filterContext.ExceptionHandled)
    {
        return;
    }
    filterContext.Result = new ViewResult
    {
        ViewName = ...
    };
    filterContext.ExceptionHandled = true;
}

这允许您重定向到带有引用异常的消息的自定义错误页面。

我使用了OnException覆盖,因为我有几个项目引用到一个有一个控制器来处理错误:

安全/HandleErrorsController.cs

protected override void OnException(ExceptionContext filterContext) 
{
    MyLogger.Error(filterContext.Exception);   //method for log in EventViewer
    if (filterContext.ExceptionHandled)
        return;
    filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
    filterContext.Result = new JsonResult
    {
        Data = new
        {
            Success = false, 
            Error = "Please report to admin.",
            ErrorText = filterContext.Exception.Message,
            Stack = filterContext.Exception.StackTrace
        },
        JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
    filterContext.ExceptionHandled = true;
}

所有这样的问题都不是很有建设性,因为答案总是"视情况而定",因为处理错误处理的方法太多了。

许多人喜欢使用HandleError方法,因为任何异常基本上都是不可恢复的。我的意思是,如果你不能返回对象,你该怎么办?无论如何,你都会给他们显示一个错误,对吧?

问题变成了,您希望如何向他们显示错误。如果向他们显示一个错误页面是可以接受的,那么HandleError工作得很好,并且提供了一个简单的地方来记录错误。如果您正在使用Ajax或想要更花哨的东西,那么您需要开发一种方法来实现这一点。

你谈论一个DataProvider类。这基本上就是你的Repository。为什么不将其构建到您的存储库中呢?