2011-08-17 76 views
7

我想知道全局错误(不一定是错误,也可以是成功消息)处理程序的最佳实现是什么?让我用一个例子打破它给你:ASP.NET MVC - 全局错误处理的方法?

  1. 用户试图删除记录
  2. 删除失败,并记录错误
  3. 用户重定向到另一个页面
  4. 显示错误消息的用户(使用HtmlHelper或其他东西,不希望它是一个特定的错误页面)

我只是好奇你们的想法。我一直在考虑TempData,ViewData和Session,但他们都有自己的优点和缺点。

TIA!

UPDATE:

我将展示一个例子就是我恰恰意味着,也许我还不够清楚。 这是在用户删除记录时添加消息的方法示例。 如果用户成功,用户重定向到另一个页面

public ActionResult DeleteRecord(Record recordToDelete) 
{ 
    // If user succeeds deleting the record 
    if (_service.DeleteRecord(recordToDelete) 
    { 
     // Add success message 
     MessageHandler.AddMessage(Status.SUCCESS, "A message to user"); 

     // And redirect to list view 
     return RedirectToAction("RecordsList"); 
    } 
    else 
    { 
     // Else return records details view 
     return View("RecordDetails", recordToDelete); 
    } 
} 

并在视图“RecordsList”,这将是有点酷,以显示一个或的HtmlHelper东西的所有消息(错误和成功消息)。

<%= Html.RenderAllMessages %> 

这可以通过很多方式实现,我只是好奇你们会做什么。

更新2:

我已创建自定义错误(消息)的处理程序。如果向下滚动,您可以看到代码。

+0

这是一种奇怪的。在大多数情况下,缺少错误信息表示成功。成功消息在大多数情况下是冗余的。如果出现错误,你会保持在同一页面上,所以我想我仍然没有得到你想要做的逻辑。 –

+1

我不同意,我认为告诉用户一个操作已经成功是非常重要的,否则用户可能会感到困惑。但我也明白你的意思,当失败时通知用户更重要。 –

+1

已完成的所有可用性研究都不同意你的观点。这是一个非常强调的事情。如果没有问题,用户不希望被打扰。 –

回答

2

我很困惑这些步骤:

  • 删除失败和错误会记录
  • 用户重定向到另一个页面

当错误发生时你为什么会重定向用户?这没有任何意义,除非我误解了某些东西。

一般来说,我遵循以下原则:

  • 错误表单提交(如HTTP POST):检查ModelState.IsValid并返回相同的查看和渲染错误出与@Html.ValidationSummary()
  • 错误与AJAX请致电:返回JsonResult(如@Tomas所述),并使用基本的客户端脚本检查JSON并显示结果
  • dom错误AIN /业务:抛出自定义异常,并让控制器赶上他们,并加入到ModelState如上
+0

我知道这听起来很奇怪,但让我们说一下你的观看细节。比方说,你删除记录,你想重定向用户到所有记录的列表视图,并显示一条消息给用户。当然,你可以使用TempData,但是使用某种通用的错误处理会很好,即使涉及请求,它也会记录所有消息。 –

+0

我已更新我的帖子,更详细的例子。 –

+0

@Kristoffer - 我明白了,你不是真的在谈论错误信息,而是在谈论自定义信息。 TempData是您唯一的选择。当没有登录的用户试图保存一些数据时,我会这样做。我在TempData中查找数据,将用户重定向到登录名,然后再返回并预先填写表单。我通过自定义操作过滤器来做到这一点。 – RPM1984

1

我更喜欢将我的服务器层编写为一个发布JSON的API - 在ASP.NET MVC中这非常简单 - 您只需创建一堆嵌套的匿名对象,然后return Json(data);即可。 JSON对象然后被客户层使用,它由html,css和javascript组成(我使用jQuery很多,但您可能更喜欢其他工具)。

由于javascript是动态的,因此只需在数据对象上拥有一个属性status,客户端脚本就可以解释该属性,并根据需要显示状态或错误消息。

例如,考虑以下操作方法:

public ActionResult ListStuff() 
{ 
    var stuff = Repo.GetStuff(); 

    return Json(new { status = "OK", thestuff = stuff }); 
} 

这将在下面的格式返回JSON:

{ "status": "OK", "thestuf": [{ ... }, { ... }] } 

其中...是用于stuff属性的占位符。现在,如果我想的错误处理,我可以做

try 
{ 
    var stuff = Repo.GetStuff(); 
    return Json(new { status = "OK", thestuff = stuff}); 
} 
catch (Exception ex) 
{ 
    Log.Error(ex); 
    return Json(new { status = "Fail", reason = ex.Message }); 
} 

由于JavaScript是动态的,也不要紧,这两个匿名对象不具有相同的属性。根据status的值,我只会查找实际存在的属性。

这可以实现的,即使你创建你自己的行动结果类,扩展JsonResult并自动添加状态属性越好。例如,您可以为失败的请求创建一个,这些失败的请求会在构造函数中接受一个异常,并为成功的请求创建一个,而不是使用匿名对象。

+3

有没有听说过HTTP状态码? –

+0

是的,这是做到这一点的一种方式。当我使用javascript时,我经常倾向于使用这种技术,但是由于涉及到HTTP请求,因此在此示例之上的场景无法工作(?)。这就是为什么我很好奇如何通过HTTP请求来保存错误的实现。 –

+0

@Max:我更喜欢使用HTTP状态代码来处理那些*我没有预见到的事情*。我的JSON对象中的状态并不是“请求失败,因为服务器错误”,因为“您尝试执行的操作不起作用,但这不是因为应用程序出现错误。” –

0

如果你要做的就是将用户重定向到另一个页面,则可以使用任何ActionMethod做所以只是重定向到它。

如果你想有一个全球性的错误,如500或403或其他一些错误,那么MVC 3默认模板为您创建一个_Error.cshtml页面并注册在global.asax错误处理。

如果你想捕获特定的错误,那么你就可以在同一个地方注册其他处理程序,并告诉系统使用该错误,错误页面。

+0

嗨!我不知道这是我想要的。我用更详细的例子更新了我的文章。谢谢! –

4

只是为了好玩,我创建的工程相当多的TempData自己的自定义错误(消息)处理器,但与小的差异,这种处理器是可访问的遍布应用。

我不打算去解释代码每一个步骤,但总括起来,我用IHttpModule的火灾为每个请求和会话保存数据的方法。以下是代码,您可以随时编辑或提供改进建议。

的Web.config(定义模块)

<httpModules> 
    <add name="ErrorManagerModule" type="ErrorManagerNamespace.ErrorManager"/> 
</httpModules> 

<system.webServer> 
    <modules runAllManagedModulesForAllRequests="true"> 
    <add name="ErrorManagerModule" type="ErrorManagerNamespace.ErrorManager"/> 
    </modules> 
</system.webServer> 

ErrorManager.cs(错误管理处理程序代码)

public class ErrorManager : IRequiresSessionState, IHttpModule 
{ 
    private const string SessionKey = "ERROR_MANAGER_SESSION_KEY"; 

    public enum Type 
    { 
     None, 
     Warning, 
     Success, 
     Error 
    } 

    /* 
    * 
    * Public methods 
    * 
    */ 

    public void Dispose() 
    { 
    } 

    public void Init(HttpApplication context) 
    { 
     context.AcquireRequestState += new EventHandler(Initiliaze); 
    } 

    public static IList<ErrorModel> GetErrors(ErrorManager.Type type = Type.None) 
    { 
     // Get all errors from session 
     var errors = GetErrorData(); 

     // Destroy Keep alive 
     // Decrease all errors request count 
     foreach (var error in errors.Where(o => type == ErrorManager.Type.None || o.ErrorType == type).ToList()) 
     { 
      error.KeepAlive = false; 
      error.IsRead = true; 
     } 

     // Save errors to session 
     SaveErrorData(errors); 

     //return errors; 
     return errors.Where(o => type == ErrorManager.Type.None || o.ErrorType == type).ToList(); 
    } 

    public static void Add(ErrorModel error) 
    { 
     // Get all errors from session 
     var errors = GetErrorData(); 
     var result = errors.Where(o => o.Key.Equals(error.Key, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); 

     // Add error to collection 
     error.IsRead = false; 

     // Error with key is already associated 
     // Remove old error from collection 
     if (result != null) 
      errors.Remove(result); 

     // Add new to collection 
     // Save errors to session 
     errors.Add(error); 
     SaveErrorData(errors); 
    } 

    public static void Add(string key, object value, ErrorManager.Type type = Type.None, bool keepAlive = false) 
    { 
     // Create new error 
     Add(new ErrorModel() 
     { 
      IsRead = false, 
      Key = key, 
      Value = value, 
      KeepAlive = keepAlive, 
      ErrorType = type 
     }); 
    } 

    public static void Remove(string key) 
    { 
     // Get all errors from session 
     var errors = GetErrorData(); 
     var result = errors.Where(o => o.Key.Equals(key, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); 

     // Error with key is in collection 
     // Remove old error 
     if (result != null) 
      errors.Remove(result); 

     // Save errors to session 
     SaveErrorData(errors); 
    } 

    public static void Clear() 
    { 
     // Clear all errors 
     HttpContext.Current.Session.Remove(SessionKey); 
    } 

    /* 
    * 
    * Private methods 
    * 
    */ 

    private void Initiliaze(object o, EventArgs e) 
    { 
     // Get context 
     var context = ((HttpApplication)o).Context; 

     // If session is ready 
     if (context.Handler is IRequiresSessionState || 
      context.Handler is IReadOnlySessionState) 
     { 
      // Load all errors from session 
      LoadErrorData(); 
     } 
    } 

    private static void LoadErrorData() 
    { 
     // Get all errors from session 
     var errors = GetErrorData().Where(o => !o.IsRead).ToList(); 

     // If KeepAlive is set to false 
     // Mark error as read 
     foreach (var error in errors) 
     { 
      if (error.KeepAlive == false) 
       error.IsRead = true; 
     } 

     // Save errors to session 
     SaveErrorData(errors); 
    } 

    private static void SaveErrorData(IList<ErrorModel> errors) 
    { 
     // Make sure to remove any old errors 
     HttpContext.Current.Session.Remove(SessionKey); 
     HttpContext.Current.Session.Add(SessionKey, errors); 
    } 

    private static IList<ErrorModel> GetErrorData() 
    { 
     // Get all errors from session 
     return HttpContext.Current.Session[SessionKey] 
      as IList<ErrorModel> ?? 
      new List<ErrorModel>(); 
    } 

    /* 
    * 
    * Model 
    * 
    */ 

    public class ErrorModel 
    { 
     public string Key { get; set; } 
     public object Value { get; set; } 
     public bool KeepAlive { get; set; } 
     internal bool IsRead { get; set; } 
     public Type ErrorType { get; set; } 
    } 

HtmlHelperExtension。CS(渲染错误的扩展方法)

public static class HtmlHelperExtension 
{ 
    public static string RenderMessages(this HtmlHelper obj, ErrorManager.Type type = ErrorManager.Type.None, object htmlAttributes = null) 
    { 
     var builder = new TagBuilder("ul"); 
     var errors = ErrorManager.GetErrors(type); 

     // If there are no errors 
     // Return empty string 
     if (errors.Count == 0) 
      return string.Empty; 

     // Merge html attributes 
     builder.MergeAttributes(new RouteValueDictionary(htmlAttributes), true); 

     // Loop all errors 
     foreach (var error in errors) 
     { 
      builder.InnerHtml += String.Format("<li class=\"{0}\"><span>{1}</span></li>", 
       error.ErrorType.ToString().ToLower(), 
       error.Value as string); 
     } 

     return builder.ToString(); 
    } 
} 

使用情况产生错误

// This will only be available for one request 
ErrorManager.Add("Key", "An error message", ErrorManager.Type.Error); 

// This will be available for multiple requests 
// When error is read, it will be removed 
ErrorManager.Add("Key", "An error message", ErrorManager.Type.Error, true); 

// Remove an error 
ErrorManager.Remove("AnotherKey"); 

// Clear all error 
ErrorManager.Clear(); 

使用的渲染错误

// This will render all errors 
<%= Html.RenderMessages() %> 

// This will just render all errors with type "Error" 
<%= Html.RenderMessages(ErrorManager.Type.Error) %>