ASP.NET MVC下基于异常处理的完整解决方案

EntLib的异常处理应用块(Exception Handling Application Block)是一个不错的异常处理框架,它使我们可以采用配置的方式来定义异常处理策略。而ASP.NET MVC是一个极具可扩展开发框架,在这篇文章中我将通过它的扩展实现与EntLib的集成,并提供一个完整的解决异常处理解决方案。[源代码从这里下载]

目录
一、基本异常处理策略
二、通过自定义Action处理异常
三、通过配置的Error View处理异常
四、自定义ActionInvoker:ExceptionActionInvoker
五、自定义Controller:BaseController

一、基本异常处理策略

我们首先来讨论我们的解决方案具体采用的异常处理策略:

  • 对于执行Controller的某个Action方法抛出的异常,我们会按照指定配置策略进行处理。我们可以采取日志记录、异常替换和封装这些常用的异常处理方式;
  • 对于处理后的异常,如果异常处理策略规定需要将其抛出,则会自动重定向到与异常类型匹配的出错页面。我们会维护一个异常类型和Error View的匹配关系;
  • 对于处理后的异常,如果异常处理策略规定需要将其抛出,则会执行与当前Action操作相匹配的错误处理Action进行处理。异常处理Action方法默认采用“On{Action}Error”这样的命名规则,而当前上下文会与异常处理操作方法的参数进行绑定。除次之外,我们会设置当前ModelState的错误信息;
  • 如果用户不曾定义相应的异常处理Action,依然采用“错误页面重定向”方式进行异常处理。

二、通过自定义Action处理异常

为了让读者对上面介绍的异常处理页面有一个深刻的理解,我们来进行一个实例演示。该实例用于模拟用户登录,我们定义了如下一个只包含用户名和密码两个属性的Model:LoginInfoModel。

   1: namespace Artech.Mvc.ExceptionHandling.Models
   2: {
   3:     public class LoginInfo
   4:     {
   5:         [Display(Name ="User Name")]
   6:         [Required(ErrorMessage = "User Name is manadatory!")]
   7:         public string UserName { get; set; }
   8:  
   9:         [Display(Name = "Password")]
  10:         [DataType(DataType.Password)]
  11:         [Required(ErrorMessage = "Password is manadatory!")]
  12:         public string Password { get; set; }
  13:     }
  14: }

我们定义了如下一个AccountController,它是我们自定义的BaseController的子类。AccountController在构造的时候调用基类构造函数指定的参数代表异常处理策略的配置名称。SignIn方法代表用于进行“登录”的操作,而OnSignInError就表示该操作对应的异常处理操作。如果在SignIn操作中抛出的异常经过处理后无需再抛出,则会通过调用OnSignInError,而此时ModelState已经被设置了相应的错误消息。

   1: public class AccountController : BaseController
   2: {
   3:     public AccountController()
   4:         : base("myPolicy")
   5:     { }
   6:  
   7:     public ActionResult SignIn()
   8:     {
   9:         return View(new LoginInfo());
  10:     }
  11:     [HttpPost]
  12:     public ActionResult SignIn(LoginInfo loginInfo)
  13:     {
  14:         if (!ModelState.IsValid)
  15:         {
  16:             return this.View(new LoginInfo { UserName = loginInfo.UserName });
  17:         }
  18:  
  19:         if (loginInfo.UserName != "Foo")
  20:         {
  21:             throw new InvalidUserNameException();
  22:         }
  23:  
  24:         if (loginInfo.Password != "password")
  25:         {
  26:             throw new UserNamePasswordNotMatchException();
  27:         }
  28:  
  29:         ViewBag.Message = "Authentication Succeeds!";
  30:         return this.View(new LoginInfo { UserName = loginInfo.UserName });
  31:     }
  32:  
  33:     public ActionResult OnSignInError(string userName)
  34:     {
  35:         return this.View(new LoginInfo { UserName = userName });
  36:     }
  37: }

具体定义在SignIn操作方法中的认证逻辑是这样的:如果用户名不是“Foo”则抛出InvalidUserNameException异常;如果密码不是“password”则抛出UserNamePasswordNotMatchException异常。下面是SignIn操作对应的View的定义:

   1: @model Artech.Mvc.ExceptionHandling.Models.LoginInfo
   2: @{
   3:     ViewBag.Title = "SignIn";
   4: }
   5: @Html.ValidationSummary()
   6: @if (ViewBag.Messages != null)
   7: { 
   8:     @ViewBag.Messages
   9: }
  10: @using (Html.BeginForm())
  11: { 
  12:     @Html.EditorForModel()
  13:     <input type="submit" value="SignIn" />
  14: }

在AccountController初始化时指定的异常处理策略“myPolicy”定义在如下的配置中。我们专门针对SignIn操作方法抛出的InvalidUserNameException和UserNamePasswordNotMatchException进行了处理,而ErrorMessageSettingHandler是我们自定义的异常处理器,它仅仅用于设置错误消息。如下面的代码片断所示,如果上述的这两种类型的异常被抛出,最终的错误消息会被指定为“User name does not exist!”和“User name does not match password!”。

   1: <exceptionHandling>
   2:   <exceptionPolicies>
   3:     <add name="myPolicy">
   4:       <exceptionTypes>
   5:         <add name="InvalidUserNameException" 
   6:                type="Artech.Mvc.ExceptionHandling.Models.InvalidUserNameException, Artech.Mvc.ExceptionHandling"
   7:              postHandlingAction="None">
   8:           <exceptionHandlers>
   9:             <add name="ErrorMessageSettingHandler"
  10:                  type="Artech.Mvc.ExceptionHandling.ErrorMessageSettingHandler, Artech.Mvc.ExceptionHandling"
  11:                  errorMessage="User name does not exist!"/>
  12:           </exceptionHandlers>
  13:         </add>
  14:         <add name="UserNamePasswordNotMatchException" 
  15:                type="Artech.Mvc.ExceptionHandling.Models.UserNamePasswordNotMatchException, Artech.Mvc.ExceptionHandling"
  16:              postHandlingAction="None">
  17:           <exceptionHandlers>
  18:             <add name="ErrorMessageSettingHandler"
  19:                  type="Artech.Mvc.ExceptionHandling.ErrorMessageSettingHandler, Artech.Mvc.ExceptionHandling"
  20:                  errorMessage="User name does not match password!"/>
  21:           </exceptionHandlers>
  22:         </add>          
  23:       </exceptionTypes>
  24:     </add>
  25:   </exceptionPolicies>
  26: </exceptionHandling>

现在我们通过路由映射将AccountController和Sign设置为默认Controller和Action后,开启我们的应用程序。在输入错误的用户名和错误明码的情况下在ValidationSummary中将自动得到相应的错误消息。

image

三、通过配置的Error View处理异常

在上面的配置中,针对InvalidUserNameException和UserNamePasswordNotMatchException这两种异常类型的配置策略都将PostHandlingAction属性设置为“None”,意味着不会将原来的异常和处理后的异常进行重新抛出。现在我们将该属性设置为“ThrowNewException”,意味着我们会将处理后的异常重新抛出来。

   1: <exceptionHandling>
   2:   <exceptionPolicies>
   3:     <add name="myPolicy">
   4:       <exceptionTypes>
   5:         <add name="InvalidUserNameException" type="Artech.Mvc.ExceptionHandling.Models.InvalidUserNameException, Artech.Mvc.ExceptionHandling"
   6:              postHandlingAction="ThrowNewException">
   7:          ...
   8:         <add name="UserNamePasswordNotMatchException" type="Artech.Mvc.ExceptionHandling.Models.UserNamePasswordNotMatchException, Artech.Mvc.ExceptionHandling"
   9:              postHandlingAction="ThrowNewException">
  10:           ...
  11:         </add>          
  12:       </exceptionTypes>
  13:     </add>
  14:   </exceptionPolicies>
  15: </exceptionHandling>

按照我们上面的异常处理策略,在这种情况下我们将采用“错误页面”的方式来进行异常处理。也HandleErrorAttribute的处理方式类似,我们支持异常类型和Error View之间的匹配关系,而这是通过类似于如下的配置来定义的。值得一提的是,这里的异常类型是经过处理后重新抛出的异常。

   1: <artech.exceptionHandling>
   2:   <add exceptionType="Artech.Mvc.ExceptionHandling.Models.InvalidUserNameException, Artech.Mvc.ExceptionHandling"
   3:         errorView="InvalideUserNameError"/>
   4:   <add exceptionType="Artech.Mvc.ExceptionHandling.Models.UserNamePasswordNotMatchException, Artech.Mvc.ExceptionHandling"
   5:         errorView="UserNamePasswordNotMatchError"/>
   6: </artech.exceptionHandling>

如上面的配置所示,我们为InvalidUserNameException和UserNamePasswordNotMatchException这两种异常类型定义了不同的Error View,分别是“InvalideUserNameError”和“UserNamePasswordNotMatchError”,详细定义如下所示:

   1: @{
   2:     Layout = null;
   3: }
   4: <!DOCTYPE html>
   5: <html>
   6: <head>
   7:     <title>Error</title>
   8: </head>
   9: <body>
  10:     <p style="color:Red; font-weight:bold">Sorry,the user name you specify does not exist!</p>
  11: </body>
  12: </html>
  13:  
  14: @{
  15:     Layout = null;
  16: }
  17: <!DOCTYPE html>
  18: <html>
  19: <head>
  20:     <title>Error</title>
  21: </head>
  22: <body>
  23:     <p style="color:Red; font-weight:bold">Sorry, The password does not match the given user name!</p>
  24: </body>
  25: </html>

现在我们按照上面的方式运行我们的程序,在分别输入错误的用户名和密码的情况下会自动显现相应的错误页面。

image

四、自定义ActionInvoker:ExceptionActionInvoker

对于上述的两种不同的异常处理方式最终是通过自定义的ActionInvoker来实现的,我们将其命名为ExceptionActionInvoker。如下面的代码片断所式,ExceptionActionInvoker直接继承自ControllerActionInvoker。属性ExceptionPolicy是一个基于指定的异常策略名称创建的ExceptionPolicyImpl 对象,用于针对EntLib进行的异常处理。而属性GetErrorView是一个用于获得作为错误页面的ViewResult对象的委托。整个异常处理的核心定义在InvokeAction方法中,该方法中指定的handleErrorActionName参数代表的是“异常处理操作名称”,整个方法就是按照上述的异常处理策略实现的。

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Web;
   5: using System.Web.Mvc;
   6: using Artech.Mvc.ExceptionHandling.Configuration;
   7: using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
   8: using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
   9: namespace Artech.Mvc.ExceptionHandling
  10: {
  11:     public class ExceptionActionInvoker: ControllerActionInvoker
  12:     {
  13:         protected ExceptionHandlingSettings ExceptionHandlingSettings{get; private set;}
  14:         protected virtual Func<string, HandleErrorInfo, ViewResult> GetErrorView { get; private set; }
  15:         public ExceptionPolicyImpl ExceptionPolicy { get; private set; }
  16:         public ExceptionActionInvoker(string exceptionPolicy,Func<string, HandleErrorInfo, ViewResult> getErrorView)
  17:         {
  18:             this.ExceptionPolicy = EnterpriseLibraryContainer.Current.GetInstance<ExceptionPolicyImpl>(exceptionPolicy);
  19:             this.GetErrorView = getErrorView;
  20:             this.ExceptionHandlingSettings = ExceptionHandlingSettings.GetSection();
  21:         }
  22:  
  23:         public override bool InvokeAction(ControllerContext controllerContext, string handleErrorActionName)
  24:         {
  25:             ExceptionContext exceptionContext = controllerContext as ExceptionContext;
  26:             if (null == exceptionContext)
  27:             {
  28:                 throw new ArgumentException("The controllerContext must be ExceptionContext!", "controllerContext");
  29:             }
  30:             try
  31:             {
  32:                 exceptionContext.ExceptionHandled = true;
  33:                 if (this.ExceptionPolicy.HandleException(exceptionContext.Exception))
  34:                 {
  35:                     HandleRethrownException(exceptionContext);
  36:                 }
  37:                 else
  38:                 {
  39:                     if (ExceptionHandlingContext.Current.Errors.Count == 0)
  40:                     {
  41:                         ExceptionHandlingContext.Current.Errors.Add(exceptionContext.Exception.Message);
  42:                     }
  43:                     ControllerDescriptor controllerDescriptor = this.GetControllerDescriptor(exceptionContext);
  44:                     ActionDescriptor handleErrorAction = FindAction(exceptionContext, controllerDescriptor, handleErrorActionName);
  45:                     if (null != handleErrorAction)
  46:                     {
  47:                         IDictionary<string, object> parameters = GetParameterValues(controllerContext, handleErrorAction);
  48:                         exceptionContext.Result = this.InvokeActionMethod(exceptionContext, handleErrorAction, parameters);
  49:                     }
  50:                     else
  51:                     {
  52:                         HandleRethrownException(exceptionContext);
  53:                     }
  54:                 }
  55:                 return true;
  56:             }
  57:             catch (Exception ex)
  58:             {
  59:                 exceptionContext.Exception = ex;
  60:                 HandleRethrownException(exceptionContext);
  61:                 return true;
  62:             }
  63:         }
  64:         protected virtual void HandleRethrownException(ExceptionContext exceptionContext)
  65:         {
  66:             string errorViewName = this.GetErrorViewName(exceptionContext.Exception.GetType());
  67:             string controllerName = (string)exceptionContext.RouteData.GetRequiredString("controller");
  68:             string action = (string)exceptionContext.RouteData.GetRequiredString("action");
  69:             HandleErrorInfo handleErrorInfo = new HandleErrorInfo(exceptionContext.Exception, controllerName, action);
  70:             exceptionContext.Result = this.GetErrorView(errorViewName, handleErrorInfo);
  71:         }
  72:         protected string GetErrorViewName(Type exceptionType)
  73:         {
  74:             ExceptionErrorViewElement element = ExceptionHandlingSettings.ExceptionErrorViews
  75:                 .Cast<ExceptionErrorViewElement>().FirstOrDefault(el=>el.ExceptionType == exceptionType);
  76:             if(null != element)
  77:             {
  78:                 return element.ErrorView;
  79:             }
  80:             if(null== element && null != exceptionType.BaseType!= null)
  81:             {
  82:                 return GetErrorViewName(exceptionType.BaseType);
  83:             }
  84:             else
  85:             {
  86:                 return "Error";
  87:             }
  88:         }
  89:     }
  90: }

五、自定义Controller:BaseController

ExceptionActionInvoker最终在我们自定义的Controller基类BaseController中被调用的。ExceptionActionInvoker对象在构造函数中被初始化,并在重写的OnException方法中被调用。

   1: using System;
   2: using System.Web.Mvc;
   3: namespace Artech.Mvc.ExceptionHandling
   4: {
   5:     public abstract class BaseController : Controller
   6:     {
   7:         public BaseController(string exceptionPolicy)
   8:         {
   9:             Func<string, HandleErrorInfo, ViewResult> getErrorView = (viewName, handleErrorInfo) => this.View(viewName, handleErrorInfo);
  10:             this.ExceptionActionInvoker = new ExceptionActionInvoker(exceptionPolicy,getErrorView);
  11:         }
  12:         public BaseController(ExceptionActionInvoker actionInvoker)
  13:         {
  14:             this.ExceptionActionInvoker = actionInvoker;
  15:         }
  16:  
  17:         public virtual ExceptionActionInvoker ExceptionActionInvoker { get; private set; }
  18:  
  19:         protected virtual string GetHandleErrorActionName(string actionName)
  20:         {
  21:             return string.Format("On{0}Error", actionName);
  22:         }
  23:  
  24:         protected override void OnException(ExceptionContext filterContext)
  25:         {
  26:             using (ExceptionHandlingContextScope contextScope = new ExceptionHandlingContextScope(filterContext))
  27:             {
  28:                 string actionName = RouteData.GetRequiredString("action");
  29:                 string handleErrorActionName = this.GetHandleErrorActionName(actionName);
  30:                 this.ExceptionActionInvoker.InvokeAction(filterContext, handleErrorActionName);
  31:                 foreach (var error in ExceptionHandlingContext.Current.Errors)
  32:                 {
  33:                     ModelState.AddModelError(Guid.NewGuid().ToString() ,error.ErrorMessage);
  34:                 }
  35:             }
  36:         }
  37:     }
  38: }

值得一提的是:整个OnException方法中的操作都在一个ExceptionHandlingContextScope中进行的。顾名思义, 我们通过ExceptionHandlingContextScope为ExceptionHandlingContext创建了一个范围。ExceptionHandlingContext定义如下,我们可以通过它获得当前的ExceptionContext和ModelErrorCollection,而静态属性Current返回当前的ExceptionHandlingContext对象。

   1: public class ExceptionHandlingContext
   2: {
   3:     [ThreadStatic]
   4:     private static ExceptionHandlingContext current;
   5:  
   6:     public ExceptionContext ExceptionContext { get; private set; }
   7:     public ModelErrorCollection Errors { get; private set; }
   8:  
   9:     public ExceptionHandlingContext(ExceptionContext exceptionContext)
  10:     {
  11:         this.ExceptionContext = exceptionContext;
  12:         this.Errors = new ModelErrorCollection();
  13:     }
  14:     public static ExceptionHandlingContext Current
  15:     {
  16:         get { return current; }
  17:         set { current = value; }
  18:     }
  19: }

在BaseController的OnException方法中,当执行了ExceptionActionInvoker的InvokeAction之后,我们会将当前ExceptionHandlingContext的ModelError转移到当前的ModelState中。这就是为什么我们会通过ValidationSummary显示错误信息的原因。对于我们的例子来说,错误消息的指定是通过如下所示的ErrorMessageSettingHandler 实现的,而它仅仅将指定的错误消息添加到当前ExceptionHandlingContext的Errors属性集合中而已。

   1: [ConfigurationElementType(typeof(ErrorMessageSettingHandlerData))]
   2: public class ErrorMessageSettingHandler : IExceptionHandler
   3: {
   4:     public string ErrorMessage { get; private set; }
   5:     public ErrorMessageSettingHandler(string errorMessage)
   6:     {
   7:         this.ErrorMessage = errorMessage;
   8:     }
   9:     public Exception HandleException(Exception exception, Guid handlingInstanceId)
  10:     {
  11:         if (null == ExceptionHandlingContext.Current)
  12:         {
  13:             throw new InvalidOperationException("...");
  14:         }
  15:  
  16:         if (string.IsNullOrEmpty(this.ErrorMessage))
  17:         {
  18:             ExceptionHandlingContext.Current.Errors.Add(exception.Message);
  19:         }
  20:         else
  21:         {
  22:             ExceptionHandlingContext.Current.Errors.Add(this.ErrorMessage);
  23:         }
  24:         return exception;
  25:     }
  26: }  
posted @ 2012-01-10 16:48 Artech 阅读(3030) 评论(29) 编辑 收藏

 回复 引用 查看   
#1楼[楼主] 2012-01-10 16:54 Artech      
 回复 引用 查看   
#2楼 2012-01-10 17:11 汤姆大叔      
不错,顶一下
不过为了异常处理,而动ActionInvoker,感觉有点大动干戈了
对于action的异常处理,在ExceptionFilter里扩展就足够了
对于Model的异常处理,Model bind的4种机制随便扩展一下,应该都能很容易达到了同样的效果
除非在架构级别的全局考虑,或还有其它深层次的考虑,一般不建议自定义ActionInvoker。

 回复 引用 查看   
#3楼 2012-01-10 17:18 artwl      
先项再看
 回复 引用 查看   
#4楼 2012-01-10 17:29 testzhangsan      
Artech 写的都是精品啊,Mark 了。
 回复 引用 查看   
#5楼[楼主] 2012-01-10 17:41 Artech      
@汤姆大叔
1、这正是打算实现在开发框架的方案
2、由于涉及到Action的手工执行,自定义Actionlnvoker无疑是最为直接的方式。(我想你没仔细看自定义Actionlnvoker的作用,它仅仅用于执行OnXxxError操作,并不是用于执行普通的Action操作,并不是一般意义上的自定义ActionInvoker的应用)

 回复 引用 查看   
#6楼 2012-01-10 18:23 (张超)      
还没细看 觉得 老大出品必然精品
 回复 引用 查看   
#7楼 2012-01-10 18:26 黑曜石      
啥也不说,顶一下先
看你的博客学到不少东西,或者不熟悉的变熟悉
今天下午弄MVC3用Area时遇到个问题,莫名其妙
把原有的views,models和controllers全部删除
新建一个区域Home,如下:

global.cs里修改了下面
routes.MapRoute(
"Default", // 路由名称
"{area}/{controller}/{action}/{id}", // 带有参数的 URL
new {area="Home", controller = "Home", action = "Login", id = UrlParameter.Optional } // 参数默认值
);
HomeAreaRegistration.cs修改
context.MapRoute(
"Home_default",
"Home/{controller}/{action}/{id}",
new { controller = "Home", action = "Login", id = UrlParameter.Optional }
);
其它的没有动,运行,结果说:
找到视图“Login”或其母版视图,或没有视图引擎支持搜索的位置。搜索了以下位置:
~/Views/Home/Login.aspx
~/Views/Home/Login.ascx
~/Views/Shared/Login.aspx
~/Views/Shared/Login.ascx
~/Views/Home/Login.cshtml
~/Views/Home/Login.vbhtml
~/Views/Shared/Login.cshtml
~/Views/Shared/Login.vbhtml
此时URL是:http://localhost:3610/
后面加上一个home,视图就出现了,不输入就不行,why

 回复 引用 查看   
#8楼 2012-01-10 20:17 遗忘海岸      
^_^
 回复 引用 查看   
#9楼[楼主] 2012-01-10 20:34 Artech      
引用黑曜石:
啥也不说,顶一下先
看你的博客学到不少东西,或者不熟悉的变熟悉
今天下午弄MVC3用Area时遇到个问题,莫名其妙
把原有的views,models和controllers全部删除
新建一个区域Home,如下:

global.cs里修改了下面
routes.MapRoute(
"Default", // 路由名称
"{area}/{controller}/{action}...

我也没整明白:)

 回复 引用 查看   
#10楼[楼主] 2012-01-10 20:38 Artech      
引用artwl:先项再看

:)
引用testzhangsan:Artech 写的都是精品啊,Mark 了。

引用(张超):还没细看 觉得 老大出品必然精品

再这样说,不敢再写东西了:)

 回复 引用 查看   
#11楼[楼主] 2012-01-10 20:39 Artech      
引用遗忘海岸:^_^

:)

 回复 引用 查看   
#12楼 2012-01-10 23:00 木子博客      
之前我也有对MVC进行全局的异常处理,对Artech的方法有些不同的看法。我觉得应该从异常的原因来分析处理方法。
1、程序错误,这块如Artech本文的分析。
2、自定义的异常,这个异常信息可能是业务规则的校验失败而采取的中断,这类信息应该使用其他的方式反馈给用户。而这种异常又分为form表单提交(处理方法也可以使用本文的方法)、Ajax请求和API调用,因此要采取其他的方式。

 回复 引用 查看   
#13楼 2012-01-10 23:22 汤姆大叔      
引用Artech:
@汤姆大叔
1、这正是打算实现在开发框架的方案
2、由于涉及到Action的手工执行,自定义Actionlnvoker无疑是最为直接的方式。(我想你没仔细看自定义Actionlnvoker的作用,它仅仅用于执行OnXxxError操作,并不是用于执行普通的Action操作,并不是一般意义上的自定义ActionInvoker的应用)


我的问题正是这个哦,既然都没有用真正的Action操作,那自定义ActionInvoker的目的纯粹为了异常就太单一了。

 回复 引用 查看   
#14楼[楼主] 2012-01-11 08:06 Artech      
引用木子博客:
之前我也有对MVC进行全局的异常处理,对Artech的方法有些不同的看法。我觉得应该从异常的原因来分析处理方法。
1、程序错误,这块如Artech本文的分析。
2、自定义的异常,这个异常信息可能是业务规则的校验失败而采取的中断,这类信息应该使用其他的方式反馈给用户。而这种异常又分为form表单提交(处理方法也可以使用本文的方法)、Ajax请求和API调用,因此要采取其他的方式。

你说的没错。
其实我在写这篇文章的时候正在设计如何为AJAX请求返回怎样的一个JSON对象,故没有加进来。

 回复 引用 查看   
#15楼[楼主] 2012-01-11 08:09 Artech      
引用汤姆大叔:
引用Artech:
@汤姆大叔
1、这正是打算实现在开发框架的方案
2、由于涉及到Action的手工执行,自定义Actionlnvoker无疑是最为直接的方式。(我想你没仔细看自定义Actionlnvoker的作用,它仅仅用于执行OnXxxError操作,并不是用于执行普通的Action操作,并不是一般意义上的自定义ActionInvoker的应用)


我的问题正是这个哦,既然都没有用真正的Action操作,那自定义ActionInvoker的目的纯粹为了异常就太单一了。

由于异常处理过程中涉及到对异常处理Action操作(OnXxxError)的执行,除了不是正常请求的Action操作之外,它和普通Action没有什么区别。而ActionInvoker仅仅具有“单一”的功能,那就是执行Action操作,那么自定义ActionInvoker无疑是最为直接的解决方式。

 回复 引用 查看   
#16楼 2012-01-11 08:46 Steven Chen      
我记得你写过wcf中异常处理使用Entlib的文章,同时对于ObjectBuilder2和Unity与WCF的对象构造你也写过,期待在你即将面世的新书里能够看到将Entlib融合到WCF中的企业应用开发的实际场景,而不仅仅是一本介绍WCF的书。

或者你干脆再写一个Entlib应用的书---嗯或者是系列

 回复 引用 查看   
#17楼[楼主] 2012-01-11 08:49 Artech      
引用Steven Chen:
我记得你写过wcf中异常处理使用Entlib的文章,同时对于ObjectBuilder2和Unity与WCF的对象构造你也写过,期待在你即将面世的新书里能够看到将Entlib融合到WCF中的企业应用开发的实际场景,而不仅仅是一本介绍WCF的书。
或者你干脆再写一个Entlib应用的书---嗯或者是系列

毕竟EntLib的受众不多,所以这样书可能买不掉:)

 回复 引用 查看   
#18楼[楼主] 2012-01-11 08:58 Artech      
引用Steven Chen:
我记得你写过wcf中异常处理使用Entlib的文章,同时对于ObjectBuilder2和Unity与WCF的对象构造你也写过,期待在你即将面世的新书里能够看到将Entlib融合到WCF中的企业应用开发的实际场景,而不仅仅是一本介绍WCF的书。

或者你干脆再写一个Entlib应用的书---嗯或者是系列

1、我的书中主要的定位还是剖析WCF的设计/实现原理;
2、只要了解了WCF的底层设计原理,我们在使用的时候很容易地就会想到和实现与一些开源框架的继承。我们以EntLib几个AppBlock来说,我们可以通过自定义ErrorHandle实现与EHAB的集成;通过自定义ParameterInspector与VAB集成;通过自定义InstanceProvider与IoC(Unity)集成;通过自定义BindingElement与Security集成(加密/签名,不过用WCF自身的功能就好),而Logging可以通过扩展Audit进行审核日至,通过CallContextInitializer实现基于方法执行的追踪,通过MessageInspector实现基于Message的记录;...
3.我的书中也会涉及到很多为了解决实际项目的需要而作的扩展,而很多扩展实际上已经应用在我们作的一些应用中。

 回复 引用 查看   
#19楼[楼主] 2012-01-11 09:02 Artech      
引用Steven Chen:
我记得你写过wcf中异常处理使用Entlib的文章,同时对于ObjectBuilder2和Unity与WCF的对象构造你也写过,期待在你即将面世的新书里能够看到将Entlib融合到WCF中的企业应用开发的实际场景,而不仅仅是一本介绍WCF的书。

或者你干脆再写一个Entlib应用的书---嗯或者是系列

由于我们的框架在底层采用了EntLib,而且我们的项目大都是基于WCF的企业应用,所以我负责的开发框架的目的就是:在服务端通过WCF的扩展,在客户端通过ASP.NET MVC的扩展解决绝大部分非业务性逻辑的处理,而开发人员只需要编写业务相关的代码就可以了,这不仅仅是出于对开发效率的需要,也是出于对质量的需要。

 回复 引用 查看   
#20楼 2012-01-11 09:07 牦牛      
我也在考虑MVC下全局的异常处理方案!看看LZ的思路先
 回复 引用 查看   
#21楼[楼主] 2012-01-11 09:15 Artech      
引用牦牛:我也在考虑MVC下全局的异常处理方案!看看LZ的思路先

这是某个早晨头脑一热想出来的,肯定有一些不成熟的地方,如果你有好的建议请不吝赐教,现在正在为这块头痛:)

 回复 引用 查看   
#22楼 2012-01-11 09:37 汤姆大叔      
引用Artech:
引用牦牛:我也在考虑MVC下全局的异常处理方案!看看LZ的思路先

这是某个早晨头脑一热想出来的,肯定有一些不成熟的地方,如果你有好的建议请不吝赐教,现在正在为这块头痛:)


推荐我同事的一篇文章,ActionInvoker里有关于ExceptionFilter的处理,通过自定义全局的ExceptionFilter就可以达到同样的目的了
http://www.cnblogs.com/RobbinHan/archive/2011/11/29/2268076.html

另外在baseController重写了OnException方法,在自定义额外的ErrorAttribute的话,可能某些事情会有一些影响,一般不建议使用这种方式,而是建议继承FilterAttribute, IExceptionFilter的方式来处理。

 回复 引用 查看   
#23楼[楼主] 2012-01-11 09:43 Artech      
引用汤姆大叔:
引用Artech:
引用牦牛:我也在考虑MVC下全局的异常处理方案!看看LZ的思路先

这是某个早晨头脑一热想出来的,肯定有一些不成熟的地方,如果你有好的建议请不吝赐教,现在正在为这块头痛:)


推荐我同事的一篇文章,ActionInvoker里有关于ExceptionFilter的处理,通过自定义全局的ExceptionFilter就可以达到同样的目的了
http://www.cnblogs.com/RobbinHan/archive/2011/11/29/2268076.html

另外在baseController重写了OnE...

两种方式在我之前的两篇文章都简单实现过了(实际上本例就是定义Base Controller并重写OnException方法来实现的)
http://www.cnblogs.com/artech/archive/2011/12/13/asp-mvc-entlib-ehab.html
http://www.cnblogs.com/artech/archive/2011/12/14/asp-mvc-entlib-ehab-02.html

自定义ActionInvoker的目的在于执行另一个匹配的Action用于显示包含错误消息的View(不是Error Page),否则需要加try/catch 比如:

try
{

}
catch()
{
this.View(...);
}

 回复 引用 查看   
#24楼 2012-01-11 14:15 KarasCanvas      
学习一下
目前还是用全局异常过滤器来处理的.

 回复 引用 查看   
#25楼 2012-01-11 14:46 这一次意外      
@黑曜石
目测global注册的路由的area不对
routes.Add(
"Default",
new Route(
"{*Values}",
new RouteValueDictionary(new { controller = "Home", action = "Login" }),
null,
new RouteValueDictionary(new { area = "Home" }),
new MvcRouteHandler())
);
这样注册路由试试

 回复 引用 查看   
#26楼 2012-01-12 10:24 陈之默      
Hi Artech,你好。我是一名11年的大学软件技术应届毕业生,现在就职于一家专做测试外包公司。曾经在学校开发过多个小型项目,一直很憧憬做开发工作。一直对WCF很好奇,但看上去又觉得很难学,难以理解WCF到底能用来干什么。你的这本书《WCF技术剖析》 我适合看吗? 如果不适合,向学习WCF有什么好书或者更好的办法?
 回复 引用 查看   
#27楼[楼主] 2012-01-12 13:46 Artech      
引用陈之默:Hi Artech,你好。我是一名11年的大学软件技术应届毕业生,现在就职于一家专做测试外包公司。曾经在学校开发过多个小型项目,一直很憧憬做开发工作。一直对WCF很好奇,但看上去又觉得很难学,难以理解WCF到底能用来干什么。你的这本书《WCF技术剖析》 我适合看吗? 如果不适合,向学习WCF有什么好书或者更好的办法?

如果对.NET Framework不熟悉的话,我不推荐读者阅读《WCF技术剖析》,就目前市场上一个WCF书籍,我觉得唯一值得阅读就是《WCF服务编程》

 回复 引用 查看   
#28楼 2012-01-13 00:21 陈之默      
@Ar
引用Artech:
引用陈之默:Hi Artech,你好。我是一名11年的大学软件技术应届毕业生,现在就职于一家专做测试外包公司。曾经在学校开发过多个小型项目,一直很憧憬做开发工作。一直对WCF很好奇,但看上去又觉得很难学,难以理解WCF到底能用来干什么。你的这本书《WCF技术剖析》 我适合看吗? 如果不适合,向学习WCF有什么好书或者更好的办法?

如果对.NET Framework不熟悉的话,我不推荐读者阅读《WCF技术剖析》,就目前市场上一个WCF书籍,我觉得唯一值得阅读就是《WCF服务编程》

我查了下,这本书是有很多个 翻译版本 ,不同出版社? 哪个出版社或者译者翻译的比较好?

 回复 引用 查看   
#29楼[楼主] 2012-01-20 11:24 Artech      
引用陈之默:
@Ar
引用Artech:
引用陈之默:Hi Artech,你好。我是一名11年的大学软件技术应届毕业生,现在就职于一家专做测试外包公司。曾经在学校开发过多个小型项目,一直很憧憬做开发工作。一直对WCF很好奇,但看上去又觉得很难学,难以理解WCF到底能用来干什么。你的这本书《WCF技术剖析》 我适合看吗? 如果不适合,向学习WCF有什么好书或者更好的办法?

如果对.NET Framework不熟悉的话,我不推荐读者阅读《WCF技术剖析》,就目前市场上一个WCF书籍,我觉得唯一值得阅读就是《WCF服务编程》

我查了下,这本书是有很多个 ...

不是有多个翻译版本,而是不同版本由不同的人翻译!

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 2318291 v02d11Xtc68=