溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

ASP.NET MVC基于異常處理的解決方法

發(fā)布時(shí)間:2021-10-29 10:14:07 來(lái)源:億速云 閱讀:110 作者:柒染 欄目:編程語(yǔ)言

今天就跟大家聊聊有關(guān)ASP.NET MVC基于異常處理的解決方法,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

EntLib的異常處理應(yīng)用塊(Exception Handling Application Block)是一個(gè)不錯(cuò)的異常處理框架,它使我們可以采用配置的方式來(lái)定義異常處理策略。而ASP.NET MVC是一個(gè)***可擴(kuò)展開(kāi)發(fā)框架,在這篇文章中我將通過(guò)它的擴(kuò)展實(shí)現(xiàn)與EntLib的集成,并提供一個(gè)完整的解決異常處理解決方案。

一、基本異常處理策略

我們首先來(lái)討論我們的解決方案具體采用的異常處理策略:

對(duì)于執(zhí)行Controller的某個(gè)Action方法拋出的異常,我們會(huì)按照指定配置策略進(jìn)行處理。我們可以采取日志記錄、異常替換和封裝這些常用的異常處理方式;

對(duì)于處理后的異常,如果異常處理策略規(guī)定需要將其拋出,則會(huì)自動(dòng)重定向到與異常類(lèi)型匹配的出錯(cuò)頁(yè)面。我們會(huì)維護(hù)一個(gè)異常類(lèi)型和Error View的匹配關(guān)系;

對(duì)于處理后的異常,如果異常處理策略規(guī)定不需要將其拋出,則會(huì)執(zhí)行與當(dāng)前Action操作相匹配的錯(cuò)誤處理Action進(jìn)行處理。異常處理Action方法默認(rèn)采用“On{Action}Error”這樣的命名規(guī)則,而當(dāng)前上下文會(huì)與異常處理操作方法的參數(shù)進(jìn)行綁定。除次之外,我們會(huì)設(shè)置當(dāng)前ModelState的錯(cuò)誤信息;

如果用戶(hù)不曾定義相應(yīng)的異常處理Action,依然采用“錯(cuò)誤頁(yè)面重定向”方式進(jìn)行異常處理。

二、通過(guò)自定義Action處理異常

為了讓讀者對(duì)上面介紹的異常處理頁(yè)面有一個(gè)深刻的理解,我們來(lái)進(jìn)行一個(gè)實(shí)例演示。該實(shí)例用于模擬用戶(hù)登錄,我們定義了如下一個(gè)只包含用戶(hù)名和密碼兩個(gè)屬性的Model:LoginInfoModel。

namespaceArtech.Mvc.ExceptionHandling.Models  {  publicclassLoginInfo  {  [Display(Name ="User Name")]  [Required(ErrorMessage = "User Name is manadatory!")]  publicstringUserName { get; set; }  [Display(Name = "Password")]  [DataType(DataType.Password)]  [Required(ErrorMessage = "Password is manadatory!")]  publicstringPassword { get; set; }  }  }

我們定義了如下一個(gè)AccountController,它是我們自定義的BaseController的子類(lèi)。AccountController在構(gòu)造的時(shí)候調(diào)用基類(lèi)構(gòu)造函數(shù)指定的參數(shù)代表異常處理策略的配置名稱(chēng)。SignIn方法代表用于進(jìn)行“登錄”的操作,而OnSignInError就表示該操作對(duì)應(yīng)的異常處理操作。如果在SignIn操作中拋出的異常經(jīng)過(guò)處理后無(wú)需再拋出,則會(huì)通過(guò)調(diào)用OnSignInError,而此時(shí)ModelState已經(jīng)被設(shè)置了相應(yīng)的錯(cuò)誤消息。

publicclassAccountController : BaseController  {  publicAccountController()   base("myPolicy")5:{ }  publicActionResult SignIn()  {  returnView(newLoginInfo());  }  [HttpPost]  publicActionResult SignIn(LoginInfo loginInfo)  {  if(!ModelState.IsValid)  {  returnthis.View(newLoginInfo { UserName = loginInfo.UserName });  }  if(loginInfo.UserName != "Foo")  {  thrownewInvalidUserNameException();  }  if(loginInfo.Password != "password")  {  thrownewUserNamePasswordNotMatchException();  }  ViewBag.Message = "Authentication Succeeds!";  returnthis.View(newLoginInfo { UserName = loginInfo.UserName });  }  publicActionResult OnSignInError(stringuserName)  {  returnthis.View(newLoginInfo { UserName = userName });  }  }

具體定義在SignIn操作方法中的認(rèn)證邏輯是這樣的:如果用戶(hù)名不是“Foo”則拋出InvalidUserNameException異常;如果密碼不是“password”則拋出UserNamePasswordNotMatchException異常。下面是SignIn操作對(duì)應(yīng)的View的定義:

@model Artech.Mvc.ExceptionHandling.Models.LoginInfo  @{  ViewBag.Title = "SignIn";  }  @Html.ValidationSummary()  @if (ViewBag.Messages != null)  {   @ViewBag.Messages  }  @using (Html.BeginForm())  {   @Html.EditorForModel()<inputtype="submit"value="SignIn"/>  }

在A(yíng)ccountController初始化時(shí)指定的異常處理策略“myPolicy”定義在如下的配置中。我們專(zhuān)門(mén)針對(duì)SignIn操作方法拋出的InvalidUserNameException和UserNamePasswordNotMatchException進(jìn)行了處理,而ErrorMessageSettingHandler是我們自定義的異常處理器,它僅僅用于設(shè)置錯(cuò)誤消息。如下面的代碼片斷所示,如果上述的這兩種類(lèi)型的異常被拋出,最終的錯(cuò)誤消息會(huì)被指定為“User name does not exist!”和“User name does not match password!”。

<exceptionHandling> <exceptionPolicies> <addnameaddname="myPolicy"> <exceptionTypes> <addnameaddname="InvalidUserNameException" type="Artech.Mvc.ExceptionHandling.Models.InvalidUserNameException, Artech.Mvc.ExceptionHandling" postHandlingAction="None"> <exceptionHandlers> <addnameaddname="ErrorMessageSettingHandler" type="Artech.Mvc.ExceptionHandling.ErrorMessageSettingHandler, Artech.Mvc.ExceptionHandling" errorMessage="User name does not exist!"/> </exceptionHandlers> </add> <addnameaddname="UserNamePasswordNotMatchException" type="Artech.Mvc.ExceptionHandling.Models.UserNamePasswordNotMatchException, Artech.Mvc.ExceptionHandling" postHandlingAction="None"> <exceptionHandlers> <addnameaddname="ErrorMessageSettingHandler" type="Artech.Mvc.ExceptionHandling.ErrorMessageSettingHandler, Artech.Mvc.ExceptionHandling" errorMessage="User name does not match password!"/> </exceptionHandlers> </add> </exceptionTypes> </add> </exceptionPolicies> </exceptionHandling>

現(xiàn)在我們通過(guò)路由映射將AccountController和Sign設(shè)置為默認(rèn)Controller和Action后,開(kāi)啟我們的應(yīng)用程序。在輸入錯(cuò)誤的用戶(hù)名和錯(cuò)誤明碼的情況下在ValidationSummary中將自動(dòng)得到相應(yīng)的錯(cuò)誤消息。

ASP.NET MVC基于異常處理的解決方法

三、通過(guò)配置的Error View處理異常

在上面的配置中,針對(duì)InvalidUserNameException和UserNamePasswordNotMatchException這兩種異常類(lèi)型的配置策略都將PostHandlingAction屬性設(shè)置為“None”,意味著不會(huì)將原來(lái)的異常和處理后的異常進(jìn)行重新拋出?,F(xiàn)在我們將該屬性設(shè)置為“ThrowNewException”,意味著我們會(huì)將處理后的異常重新拋出來(lái)。

<exceptionHandling> <exceptionPolicies> <addnameaddname="myPolicy"> <exceptionTypes> <addnameaddname="InvalidUserNameException"type="Artech.Mvc.ExceptionHandling.Models.InvalidUserNameException, Artech.Mvc.ExceptionHandling"  postHandlingAction="ThrowNewException"> ...  <addnameaddname="UserNamePasswordNotMatchException"type="Artech.Mvc.ExceptionHandling.Models.UserNamePasswordNotMatchException, Artech.Mvc.ExceptionHandling"9:postHandlingAction="ThrowNewException"> ...  </add> </exceptionTypes> </add> </exceptionPolicies> </exceptionHandling>

按照我們上面的異常處理策略,在這種情況下我們將采用“錯(cuò)誤頁(yè)面”的方式來(lái)進(jìn)行異常處理。也HandleErrorAttribute的處理方式類(lèi)似,我們支持異常類(lèi)型和Error View之間的匹配關(guān)系,而這是通過(guò)類(lèi)似于如下的配置來(lái)定義的。值得一提的是,這里的異常類(lèi)型是經(jīng)過(guò)處理后重新拋出的異常

<exceptionHandling> <exceptionPolicies> <addnameaddname="myPolicy"> <exceptionTypes> <addnameaddname="InvalidUserNameException"type="Artech.Mvc.ExceptionHandling.Models.InvalidUserNameException, Artech.Mvc.ExceptionHandling"  postHandlingAction="ThrowNewException"> ...  <addnameaddname="UserNamePasswordNotMatchException"type="Artech.Mvc.ExceptionHandling.Models.UserNamePasswordNotMatchException, Artech.Mvc.ExceptionHandling"9:postHandlingAction="ThrowNewException"> ...  </add> </exceptionTypes> </add> </exceptionPolicies> </exceptionHandling>

如上面的配置所示,我們?yōu)镮nvalidUserNameException和UserNamePasswordNotMatchException這兩種異常類(lèi)型定義了不同的Error View,分別是“InvalideUserNameError”和“UserNamePasswordNotMatchError”,詳細(xì)定義如下所示:

@{  Layout = null;  }  <!DOCTYPEhtml> <html> <head> <title>Error</title> </head> <body> <pstylepstyle="color:Red; font-weight:bold">Sorry,the user name you specify does not exist!</p> </body> </html>  @{  Layout = null;  }  <!DOCTYPEhtml> <html> <head> <title>Error</title> </head> <body> <pstylepstyle="color:Red; font-weight:bold">Sorry, The password does not match the given user name!</p> </body> </html>

現(xiàn)在我們按照上面的方式運(yùn)行我們的程序,在分別輸入錯(cuò)誤的用戶(hù)名和密碼的情況下會(huì)自動(dòng)顯現(xiàn)相應(yīng)的錯(cuò)誤頁(yè)面。ASP.NET MVC基于異常處理的解決方法

四、自定義ActionInvoker:ExceptionActionInvoker

對(duì)于上述的兩種不同的異常處理方式最終是通過(guò)自定義的ActionInvoker來(lái)實(shí)現(xiàn)的,我們將其命名為ExceptionActionInvoker。如下面的代碼片斷所式,ExceptionActionInvoker直接繼承自ControllerActionInvoker。屬性ExceptionPolicy是一個(gè)基于指定的異常策略名稱(chēng)創(chuàng)建的ExceptionPolicyImpl 對(duì)象,用于針對(duì)EntLib進(jìn)行的異常處理。而屬性GetErrorView是一個(gè)用于獲得作為錯(cuò)誤頁(yè)面的ViewResult對(duì)象的委托。整個(gè)異常處理的核心定義在InvokeAction方法中,該方法中指定的handleErrorActionName參數(shù)代表的是“異常處理操作名稱(chēng)”,整個(gè)方法就是按照上述的異常處理策略實(shí)現(xiàn)的。

usingSystem;  usingSystem.Collections.Generic;  usingSystem.Linq;  usingSystem.Web;  usingSystem.Web.Mvc;  usingArtech.Mvc.ExceptionHandling.Configuration;  usingMicrosoft.Practices.EnterpriseLibrary.Common.Configuration;  usingMicrosoft.Practices.EnterpriseLibrary.ExceptionHandling;  namespaceArtech.Mvc.ExceptionHandling  {  publicclassExceptionActionInvoker: ControllerActionInvoker  {  protectedExceptionHandlingSettings ExceptionHandlingSettings{get; privateset;}  protectedvirtualFunc<string, HandleErrorInfo, ViewResult> GetErrorView { get; privateset; }  publicExceptionPolicyImpl ExceptionPolicy { get; privateset; }  publicExceptionActionInvoker(stringexceptionPolicy,Func<string, HandleErrorInfo, ViewResult> getErrorView)  {  this.ExceptionPolicy = EnterpriseLibraryContainer.Current.GetInstance<ExceptionPolicyImpl>(exceptionPolicy);  this.GetErrorView = getErrorView;  this.ExceptionHandlingSettings = ExceptionHandlingSettings.GetSection();  }  publicoverrideboolInvokeAction(ControllerContext controllerContext, stringhandleErrorActionName)  {  ExceptionContext exceptionContext = controllerContext asExceptionContext;  if(null== exceptionContext)  {  thrownewArgumentException("The controllerContext must be ExceptionContext!", "controllerContext");  }  try {  exceptionContext.ExceptionHandled = true;  if(this.ExceptionPolicy.HandleException(exceptionContext.Exception))  {  HandleRethrownException(exceptionContext);  }  else {  if(ExceptionHandlingContext.Current.Errors.Count == 0)  {  ExceptionHandlingContext.Current.Errors.Add(exceptionContext.Exception.Message);  }  ControllerDescriptor controllerDescriptor = this.GetControllerDescriptor(exceptionContext);  ActionDescriptor handleErrorAction = FindAction(exceptionContext, controllerDescriptor, handleErrorActionName);  if(null!= handleErrorAction)  {  IDictionary<string, object>parameters = GetParameterValues(controllerContext, handleErrorAction);  exceptionContext.Result = this.InvokeActionMethod(exceptionContext, handleErrorAction, parameters);  }  else {  HandleRethrownException(exceptionContext);  }  }  returntrue;  }  catch(Exception ex)  {  exceptionContext.Exception = ex;60:HandleRethrownException(exceptionContext);  returntrue;  }  }  protectedvirtualvoidHandleRethrownException(ExceptionContext exceptionContext)  {  stringerrorViewName = this.GetErrorViewName(exceptionContext.Exception.GetType());  stringcontrollerName = (string)exceptionContext.RouteData.GetRequiredString("controller");  stringaction = (string)exceptionContext.RouteData.GetRequiredString("action");  HandleErrorInfo handleErrorInfo = newHandleErrorInfo(exceptionContext.Exception, controllerName, action);70:exceptionContext.Result = this.GetErrorView(errorViewName, handleErrorInfo);  }  protectedstringGetErrorViewName(Type exceptionType)  {  ExceptionErrorViewElement element = ExceptionHandlingSettings.ExceptionErrorViews75:.Cast<ExceptionErrorViewElement>().FirstOrDefault(el=>el.ExceptionType == exceptionType);  if(null!= element)  {  returnelement.ErrorView;  }  if(null== element &&null!= exceptionType.BaseType!= null)  {  returnGetErrorViewName(exceptionType.BaseType);  }  else {  return"Error";  }  }  }  }

五、自定義Controller:BaseController

ExceptionActionInvoker最終在我們自定義的Controller基類(lèi)BaseController中被調(diào)用的。ExceptionActionInvoker對(duì)象在構(gòu)造函數(shù)中被初始化,并在重寫(xiě)的OnException方法中被調(diào)用。

usingSystem;  usingSystem.Web.Mvc;  namespaceArtech.Mvc.ExceptionHandling  {  publicabstractclassBaseController : Controller  {  publicBaseController(stringexceptionPolicy)  {  Func<string, HandleErrorInfo, ViewResult> getErrorView = (viewName, handleErrorInfo) => this.View(viewName, handleErrorInfo);  this.ExceptionActionInvoker = newExceptionActionInvoker(exceptionPolicy,getErrorView);  }  publicBaseController(ExceptionActionInvoker actionInvoker)  {  this.ExceptionActionInvoker = actionInvoker;}publicvirtualExceptionActionInvoker ExceptionActionInvoker { get; privateset; }  protectedvirtualstringGetHandleErrorActionName(stringactionName)  {  returnstring.Format("On{0}Error", actionName);  }  protectedoverridevoidOnException(ExceptionContext filterContext)  {  using(ExceptionHandlingContextScope contextScope = newExceptionHandlingContextScope(filterContext))  {  stringactionName = RouteData.GetRequiredString("action");  stringhandleErrorActionName = this.GetHandleErrorActionName(actionName);  this.ExceptionActionInvoker.InvokeAction(filterContext, handleErrorActionName);  foreach(var error inExceptionHandlingContext.Current.Errors)  {  ModelState.AddModelError(Guid.NewGuid().ToString() ,error.ErrorMessage);  }  }  }  }  }

值得一提的是:整個(gè)OnException方法中的操作都在一個(gè)ExceptionHandlingContextScope中進(jìn)行的。顧名思義, 我們通過(guò)ExceptionHandlingContextScope為ExceptionHandlingContext創(chuàng)建了一個(gè)范圍。ExceptionHandlingContext定義如下,我們可以通過(guò)它獲得當(dāng)前的ExceptionContext和ModelErrorCollection,而靜態(tài)屬性Current返回當(dāng)前的ExceptionHandlingContext對(duì)象。

publicclassExceptionHandlingContext  {  [ThreadStatic]  privatestaticExceptionHandlingContext current;  publicExceptionContext ExceptionContext { get; privateset; }  publicModelErrorCollection Errors { get; privateset; }  publicExceptionHandlingContext(ExceptionContext exceptionContext)  {  this.ExceptionContext = exceptionContext;this.Errors = newModelErrorCollection();  }  publicstaticExceptionHandlingContext Current  {  get { returncurrent; }  set { current = value;}  }  }

在BaseController的OnException方法中,當(dāng)執(zhí)行了ExceptionActionInvoker的InvokeAction之后,我們會(huì)將當(dāng)前ExceptionHandlingContext的ModelError轉(zhuǎn)移到當(dāng)前的ModelState中。這就是為什么我們會(huì)通過(guò)ValidationSummary顯示錯(cuò)誤信息的原因。對(duì)于我們的例子來(lái)說(shuō),錯(cuò)誤消息的指定是通過(guò)如下所示的ErrorMessageSettingHandler 實(shí)現(xiàn)的,而它僅僅將指定的錯(cuò)誤消息添加到當(dāng)前ExceptionHandlingContext的Errors屬性集合中而已。

[ConfigurationElementType(typeof(ErrorMessageSettingHandlerData))]  publicclassErrorMessageSettingHandler : IExceptionHandler  {  publicstringErrorMessage { get; privateset; }  publicErrorMessageSettingHandler(stringerrorMessage)  {  this.ErrorMessage = errorMessage;  }  publicException HandleException(Exception exception, Guid handlingInstanceId)  {  if(null== ExceptionHandlingContext.Current)  {  thrownewInvalidOperationException("...");  }  if(string.IsNullOrEmpty(this.ErrorMessage))  {  ExceptionHandlingContext.Current.Errors.Add(exception.Message);  }  else {  ExceptionHandlingContext.Current.Errors.Add(this.ErrorMessage);  }  returnexception;  }  }

看完上述內(nèi)容,你們對(duì)ASP.NET MVC基于異常處理的解決方法有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI