控制器中的异常处理 (ASP.NET MVC)
Posted
技术标签:
【中文标题】控制器中的异常处理 (ASP.NET MVC)【英文标题】:Exception handling in Controller (ASP.NET MVC) 【发布时间】:2013-06-07 10:26:38 【问题描述】:当您自己的代码抛出异常时,该异常是从控制器中的操作调用的,应该如何处理?我看到很多最佳实践示例,其中根本没有 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 类中,该类处理错误并返回消息以向用户显示消息。
我想知道最好的处理方法是什么?我并不总是想返回错误页面,因为某些异常不应该这样做。相反,应该以正常视图向用户显示错误消息。我之前的方法是正确的还是有更好的解决方案?
【问题讨论】:
【参考方案1】:我做了三件事来显示更多用户友好的消息:
-
利用全局异常处理程序。对于 MVC:Global.asax 中的 Application_Error。在此处了解如何使用它:http://msdn.microsoft.com/en-us/library/24395wz3(v=vs.100).aspx
我将 Exception 子类化为 UserFriendlyException。我在所有底层服务类中尽我所能抛出这个 UserFriendlyException 而不是一个普通的旧异常。我总是尝试将对用户有意义的消息放入这些自定义异常中。其主要目的是能够对 Application_Error 方法中的异常进行类型检查。对于 UserFriendlyExceptions,我只使用我在服务中设置的用户友好消息,例如“嘿!91 度不是有效的纬度值!”。如果是常规异常,则说明我没有处理某些情况,因此我会显示更通用的错误消息,例如“糟糕,出了点问题!我们会尽力解决这个问题!”。
我还创建了一个 ErrorController 负责呈现用户友好的视图或 JSON。这是将从 Application_Error 方法调用其方法的控制器。
编辑: 我想我会提到 ASP.NET Web API,因为它密切相关。因为 Web API 端点的使用者不一定是浏览器,所以我喜欢稍微不同地处理错误。我仍然使用“FriendlyException”(上面的#2),但我没有重定向到 ErrorController,而是让我的所有端点返回某种包含 Error 属性的基本类型。因此,如果异常一直冒泡到 Web API 控制器,我会确保将该错误粘贴到 API 响应的 Error 属性中。此错误消息将是从 API 控制器所依赖的类中冒出的友好消息,或者如果异常类型不是 FriendlyException,则它将是通用消息。这样,消费客户端可以简单地检查 API 响应的 Error 属性是否为空。如果存在错误,则显示一条消息,否则照常进行。好消息是,由于友好的消息概念,消息对用户来说可能比一般的“错误!”更有意义。信息。我在使用 Xamarin 编写移动应用程序时使用此策略,我可以在我的 Web 服务和我的 ios/android 应用程序之间共享我的 C# 类型。
【讨论】:
我也同意 Patrick Desjardins 的回答:重写 OnException 是处理错误的好方法。特别是如果它位于所有其他控制器都继承自的基本控制器中。【参考方案2】:使用 Asp.Net MVC,您还可以覆盖控制器的 OnException 方法。
protected override void OnException(ExceptionContext filterContext)
if (filterContext.ExceptionHandled)
return;
filterContext.Result = new ViewResult
ViewName = ...
;
filterContext.ExceptionHandled = true;
这允许您重定向到自定义错误页面,如果您愿意,可以使用引用异常的消息。
【讨论】:
可能反对者会争论一点:这个答案有什么问题? @Askolein 我也不明白。如果您像我一样认为这是一个可能的解决方案,您可以投票将其设置回 0。 我同意这是一个很好的答案。在自定义基本控制器中覆盖 OnException,并使您的所有其他控制器都继承自它。将此与用户友好的异常概念结合起来,您将获得一个很好的解决方案。 this 类似这个问题的问题呢? 一个警告 - OnException 不会捕获控制器引发的所有异常(耻辱),只会捕获那些在 Actions 和一些与操作相关的方法中引发的异常。所以 controller.initialize() 中的异常不会被捕获,但是 controller.OnActionExecuting() 中的异常会。【参考方案3】:我使用了 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;
【讨论】:
向用户返回原始异常内容(如消息和堆栈跟踪)是一个非常糟糕的主意。 向用户返回异常是不好的做法,主要是从安全角度来看【参考方案4】:像这样的所有问题都不是很有建设性,因为答案总是“视情况而定”,因为处理错误的方法太多了。
很多人喜欢使用 HandleError 方法,因为任何异常基本上都是不可恢复的。我的意思是,如果你不能归还物品,你会怎么做?无论如何,您都会向他们显示错误,对吗?
问题变成了,你想如何向他们展示错误。如果向他们显示错误页面是可以接受的,那么 HandleError 可以正常工作,并提供一个简单的地方来记录错误。如果您正在使用 Ajax 或想要更高级的东西,那么您需要开发一种方法来做到这一点。
您谈论的是 DataProvider 类。这基本上就是您的存储库。为什么不将其构建到您的存储库中?
【讨论】:
以上是关于控制器中的异常处理 (ASP.NET MVC)的主要内容,如果未能解决你的问题,请参考以下文章
在 ASP.NET Core 2 中处理异常的推荐方法? [关闭]