ASP.NET MVC是一個極具可擴展開發框架,在這篇文章中我將透過它的擴展實現與EntLib的集成,並提供一個完整的解決異常處理解決方案。
EntLib的異常處理應用程式區塊(Exception Handling Application Block)是一個不錯的例外處理框架,它使我們可以採用配置的方式來定義異常處理策略。而ASP.NET MVC是一個極具可擴展開發框架,在這篇文章中我將透過它的擴展實現與EntLib的集成,並提供一個完整的解決異常處理解決方案。
一、基本異常處理策略
#我們首先來討論我們的解決方案具體採用的例外處理策略:
對於執行Controller的某個Action方法拋出的例外,我們會依照指定設定策略來處理。我們可以採取日誌記錄、異常替換和封裝這些常用的異常處理方式;
對於處理後的異常,如果異常處理策略規定需要將其拋出,則會自動重定向到與異常類型符合的出錯頁面。我們會維護一個異常類型和Error View的匹配關係;
對於處理後的異常,如果異常處理策略規定不需要將其拋出,則會執行與當前Action操作相符的錯誤處理Action進行處理。異常處理Action方法預設採用「On{Action}Error」這樣的命名規則,而目前上下文會與異常處理操作方法的參數綁定。除此之外,我們會設定目前ModelState的錯誤訊息;
如果使用者不曾定義對應的異常處理Action,依然採用「錯誤頁面重定向」方式進行異常處理。
二、透過自訂Action處理例外
#為了讓讀者對上面介紹的例外處理頁面有一個深刻的理解,我們來進行一個實例演示。這個實例用於模擬使用者登錄,我們定義瞭如下一個只包含使用者名稱和密碼兩個屬性的Model:LoginInfoModel。
namespace Artech.Mvc.ExceptionHandling.Models { public class LoginInfo { [Display(Name ="User Name")] [Required(ErrorMessage = "User Name is manadatory!")] public string UserName { get; set; } [Display(Name = "Password")] [DataType(DataType.Password)] [Required(ErrorMessage = "Password is manadatory!")] public string Password { get; set; } } }
我們定義瞭如下一個AccountController,它是我們自訂的BaseController的子類別。 AccountController在建構的時候呼叫基底類別建構子指定的參數代表異常處理策略的配置名稱。 SignIn方法代表用於進行「登入」的操作,而OnSignInError就表示該操作對應的異常處理操作。如果在SignIn操作中拋出的異常經過處理後無需再拋出,則會透過呼叫OnSignInError,而此時ModelState已經被設定了相應的錯誤訊息。
public class AccountController BaseController { public AccountController() base("myPolicy") { } public ActionResult SignIn() { return View(new LoginInfo()); } [HttpPost] public ActionResult SignIn(LoginInfo loginInfo) { if (!ModelState.IsValid) { return this.View(new LoginInfo { UserName = loginInfo.UserName }); } if (loginInfo.UserName != "Foo") { throw new InvalidUserNameException(); } if (loginInfo.Password != "password") { throw new UserNamePasswordNotMatchException(); } ViewBag.Message = "Authentication Succeeds!"; return this.View(new LoginInfo { UserName = loginInfo.UserName }); } public ActionResult OnSignInError(string userName) { return this.View(new LoginInfo { UserName = userName }); } }
具體定義在SignIn操作方法中的認證邏輯是這樣的:如果使用者名稱不是「Foo」則拋出InvalidUserNameException異常;如果密碼不是「password」則拋出UserNamePasswordNotMatchException異常。以下是SignIn操作對應的View的定義:
@model Artech.Mvc.ExceptionHandling.Models.LoginInfo @{ ViewBag.Title = "SignIn"; } @Html.ValidationSummary() @if (ViewBag.Messages != null) { @ViewBag.Messages } @using (Html.BeginForm()) { @Html.EditorForModel() <input type="submit" value="SignIn" /> }
在AccountController初始化時指定的例外處理策略「myPolicy」定義在如下的配置中。我們專門針對SignIn操作方法拋出的InvalidUserNameException和UserNamePasswordNotMatchException進行了處理,而ErrorMessageSettingHandler是我們自訂的例外處理器,它只用於設定錯誤訊息。如下面的程式碼片斷所示,如果上述的這兩種類型的異常被拋出,最終的錯誤訊息會被指定為「User name does not exist!」和「User name does not match password!」。
<exceptionHandling> <exceptionPolicies> <add name="myPolicy"> <exceptionTypes> <add name="InvalidUserNameException" type="Artech.Mvc.ExceptionHandling.Models.InvalidUserNameException, Artech.Mvc.ExceptionHandling" postHandlingAction="None"> <exceptionHandlers> <add name="ErrorMessageSettingHandler" type="Artech.Mvc.ExceptionHandling.ErrorMessageSettingHandler, Artech.Mvc.ExceptionHandling" errorMessage="User name does not exist!"/> </exceptionHandlers> </add> <add name="UserNamePasswordNotMatchException" type="Artech.Mvc.ExceptionHandling.Models.UserNamePasswordNotMatchException, Artech.Mvc.ExceptionHandling" postHandlingAction="None"> <exceptionHandlers> <add name="ErrorMessageSettingHandler" type="Artech.Mvc.ExceptionHandling.ErrorMessageSettingHandler, Artech.Mvc.ExceptionHandling" errorMessage="User name does not match password!"/> </exceptionHandlers> </add> </exceptionTypes> </add> </exceptionPolicies> </exceptionHandling>
現在我們透過路由映射將AccountController和Sign設定為預設Controller和Action後,開啟我們的應用程式。在輸入錯誤的使用者名稱和錯誤明碼的情況下在ValidationSummary中將自動得到對應的錯誤訊息。
三、透過設定的Error View處理異常
在上面的設定中,針對InvalidUserNameException和UserNamePasswordNotMatchException這兩種異常類型的配置策略都將PostHandlingAction屬性設為“None”,意味著不會將原來的異常和處理後的異常進行重新拋出。現在我們將該屬性設為“ThrowNewException”,這意味著我們會將處理後的例外重新拋出。
<exceptionHandling> <exceptionPolicies> <add name="myPolicy"> <exceptionTypes> <add name="InvalidUserNameException" type="Artech.Mvc.ExceptionHandling.Models.InvalidUserNameException, Artech.Mvc.ExceptionHandling" postHandlingAction="ThrowNewException"> ... <add name="UserNamePasswordNotMatchException" type="Artech.Mvc.ExceptionHandling.Models.UserNamePasswordNotMatchException, Artech.Mvc.ExceptionHandling" postHandlingAction="ThrowNewException"> ... </add> </exceptionTypes> </add> </exceptionPolicies> </exceptionHandling>
按照我們上面的例外處理策略,在這種情況下我們將採用「錯誤頁面」的方式來進行異常處理。也HandleErrorAttribute的處理方式類似,我們支援異常類型和Error View之間的匹配關係,而這是透過類似於如下的配置來定義的。值得一提的是,這裡的異常類型是經過處理後重新拋出的異常。
<artech.exceptionHandling> <add exceptionType="Artech.Mvc.ExceptionHandling.Models.InvalidUserNameException, Artech.Mvc.ExceptionHandling" errorView="InvalideUserNameError"/> <add exceptionType="Artech.Mvc.ExceptionHandling.Models.UserNamePasswordNotMatchException, Artech.Mvc.ExceptionHandling" errorView="UserNamePasswordNotMatchError"/> </artech.exceptionHandling>
如上面的配置所示,我們為InvalidUserNameException和UserNamePasswordNotMatchException這兩種異常類型定義了不同的Error View,分別是“InvalideUserNameError”和“UserNamePasswordNotMatchError”,詳細定義如下圖所示:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <title>Error</title> </head> <body> <p style="colorRed; font-weightbold">Sorry,the user name you specify does not exist!</p> </body> </html> @{ Layout = null; } <!DOCTYPE html> <html> <head> <title>Error</title> </head> <body> <p style="colorRed; font-weightbold">Sorry, The password does not match the given user name!</p> </body> </html>
现在我们按照上面的方式运行我们的程序,在分别输入错误的用户名和密码的情况下会自动显现相应的错误页面。
四、自定义ActionInvoker:ExceptionActionInvoker
对于上述的两种不同的异常处理方式最终是通过自定义的ActionInvoker来实现的,我们将其命名为ExceptionActionInvoker。如下面的代码片断所式,ExceptionActionInvoker直接继承自ControllerActionInvoker。属性ExceptionPolicy是一个基于指定的异常策略名称创建的ExceptionPolicyImpl 对象,用于针对EntLib进行的异常处理。而属性GetErrorView是一个用于获得作为错误页面的ViewResult对象的委托。整个异常处理的核心定义在InvokeAction方法中,该方法中指定的handleErrorActionName参数代表的是“异常处理操作名称”,整个方法就是按照上述的异常处理策略实现的。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Artech.Mvc.ExceptionHandling.Configuration; using Microsoft.Practices.EnterpriseLibrary.Common.Configuration; using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling; namespace Artech.Mvc.ExceptionHandling { public class ExceptionActionInvoker ControllerActionInvoker { protected ExceptionHandlingSettings ExceptionHandlingSettings{get; private set;} protected virtual Func<string, HandleErrorInfo, ViewResult> GetErrorView { get; private set; } public ExceptionPolicyImpl ExceptionPolicy { get; private set; } public ExceptionActionInvoker(string exceptionPolicy,Func<string, HandleErrorInfo, ViewResult> getErrorView) { this.ExceptionPolicy = EnterpriseLibraryContainer.Current.GetInstance<ExceptionPolicyImpl>(exceptionPolicy); this.GetErrorView = getErrorView; this.ExceptionHandlingSettings = ExceptionHandlingSettings.GetSection(); } public override bool InvokeAction(ControllerContext controllerContext, string handleErrorActionName) { ExceptionContext exceptionContext = controllerContext as ExceptionContext; if (null == exceptionContext) { throw new ArgumentException("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); } } return true; } catch (Exception ex) { exceptionContext.Exception = ex; HandleRethrownException(exceptionContext); return true; } } protected virtual void HandleRethrownException(ExceptionContext exceptionContext) { string errorViewName = this.GetErrorViewName(exceptionContext.Exception.GetType()); string controllerName = (string)exceptionContext.RouteData.GetRequiredString("controller"); string action = (string)exceptionContext.RouteData.GetRequiredString("action"); HandleErrorInfo handleErrorInfo = new HandleErrorInfo(exceptionContext.Exception, controllerName, action); exceptionContext.Result = this.GetErrorView(errorViewName, handleErrorInfo); } protected string GetErrorViewName(Type exceptionType) { ExceptionErrorViewElement element = ExceptionHandlingSettings.ExceptionErrorViews .Cast<ExceptionErrorViewElement>().FirstOrDefault(el=>el.ExceptionType == exceptionType); if(null != element) { return element.ErrorView; } if(null== element && null != exceptionType.BaseType!= null) { return GetErrorViewName(exceptionType.BaseType); } else { return "Error"; } } } }
五、自定义Controller:BaseController
ExceptionActionInvoker最终在我们自定义的Controller基类BaseController中被调用的。ExceptionActionInvoker对象在构造函数中被初始化,并在重写的OnException方法中被调用。
using System; using System.Web.Mvc; namespace Artech.Mvc.ExceptionHandling { public abstract class BaseController Controller { public BaseController(string exceptionPolicy) { Func<string, HandleErrorInfo, ViewResult> getErrorView = (viewName, handleErrorInfo) => this.View(viewName, handleErrorInfo); this.ExceptionActionInvoker = new ExceptionActionInvoker(exceptionPolicy,getErrorView); } public BaseController(ExceptionActionInvoker actionInvoker) { this.ExceptionActionInvoker = actionInvoker; } public virtual ExceptionActionInvoker ExceptionActionInvoker { get; private set; } protected virtual string GetHandleErrorActionName(string actionName) { return string.Format("On{0}Error", actionName); } protected override void OnException(ExceptionContext filterContext) { using (ExceptionHandlingContextScope contextScope = new ExceptionHandlingContextScope(filterContext)) { string actionName = RouteData.GetRequiredString("action"); string handleErrorActionName = this.GetHandleErrorActionName(actionName); this.ExceptionActionInvoker.InvokeAction(filterContext, handleErrorActionName); foreach (var error in ExceptionHandlingContext.Current.Errors) { ModelState.AddModelError(Guid.NewGuid().ToString() ,error.ErrorMessage); } } } } }
值得一提的是:整个OnException方法中的操作都在一个ExceptionHandlingContextScope中进行的。顾名思义, 我们通过ExceptionHandlingContextScope为ExceptionHandlingContext创建了一个范围。ExceptionHandlingContext定义如下,我们可以通过它获得当前的ExceptionContext和ModelErrorCollection,而静态属性Current返回当前的ExceptionHandlingContext对象。
public class ExceptionHandlingContext { [ThreadStatic] private static ExceptionHandlingContext current; public ExceptionContext ExceptionContext { get; private set; } public ModelErrorCollection Errors { get; private set; } public ExceptionHandlingContext(ExceptionContext exceptionContext) { this.ExceptionContext = exceptionContext; this.Errors = new ModelErrorCollection(); } public static ExceptionHandlingContext Current { get { return current; } set { current = value; } } }
在BaseController的OnException方法中,当执行了ExceptionActionInvoker的InvokeAction之后,我们会将当前ExceptionHandlingContext的ModelError转移到当前的ModelState中。这就是为什么我们会通过ValidationSummary显示错误信息的原因。对于我们的例子来说,错误消息的指定是通过如下所示的ErrorMessageSettingHandler 实现的,而它仅仅将指定的错误消息添加到当前ExceptionHandlingContext的Errors属性集合中而已。
[ConfigurationElementType(typeof(ErrorMessageSettingHandlerData))] public class ErrorMessageSettingHandler IExceptionHandler { public string ErrorMessage { get; private set; } public ErrorMessageSettingHandler(string errorMessage) { thisErrorMessage = errorMessage; } public Exception HandleException(Exception exception, Guid handlingInstanceId) { if (null == ExceptionHandlingContextCurrent) { throw new InvalidOperationException(""); } if (stringIsNullOrEmpty(thisErrorMessage)) { ExceptionHandlingContextCurrentErrorsAdd(exceptionMessage); } else { ExceptionHandlingContextCurrentErrorsAdd(thisErrorMessage); } return exception; } }
以上是ASP.NET MVC對異常處理的解決方法總結的詳細內容。更多資訊請關注PHP中文網其他相關文章!

C#和.NET的關係是密不可分的,但它們不是一回事。 C#是一門編程語言,而.NET是一個開發平台。 C#用於編寫代碼,編譯成.NET的中間語言(IL),由.NET運行時(CLR)執行。

C#.NET依然重要,因為它提供了強大的工具和庫,支持多種應用開發。 1)C#結合.NET框架,使開發高效便捷。 2)C#的類型安全和垃圾回收機制增強了其優勢。 3).NET提供跨平台運行環境和豐富的API,提升了開發靈活性。

C#.NETisversatileforbothwebanddesktopdevelopment.1)Forweb,useASP.NETfordynamicapplications.2)Fordesktop,employWindowsFormsorWPFforrichinterfaces.3)UseXamarinforcross-platformdevelopment,enablingcodesharingacrossWindows,macOS,Linux,andmobiledevices.

C#和.NET通過不斷的更新和優化,適應了新興技術的需求。 1)C#9.0和.NET5引入了記錄類型和性能優化。 2).NETCore增強了雲原生和容器化支持。 3)ASP.NETCore與現代Web技術集成。 4)ML.NET支持機器學習和人工智能。 5)異步編程和最佳實踐提升了性能。

c#.netissutableforenterprise-levelapplications withemofrosoftecosystemdueToItsStrongTyping,richlibraries,androbustperraries,androbustperformance.however,itmaynotbeidealfoross-platement forment forment forment forvepentment offependment dovelopment toveloperment toveloperment whenrawspeedsportor whenrawspeedseedpolitical politionalitable,

C#在.NET中的編程過程包括以下步驟:1)編寫C#代碼,2)編譯為中間語言(IL),3)由.NET運行時(CLR)執行。 C#在.NET中的優勢在於其現代化語法、強大的類型系統和與.NET框架的緊密集成,適用於從桌面應用到Web服務的各種開發場景。

C#是一種現代、面向對象的編程語言,由微軟開發並作為.NET框架的一部分。 1.C#支持面向對象編程(OOP),包括封裝、繼承和多態。 2.C#中的異步編程通過async和await關鍵字實現,提高應用的響應性。 3.使用LINQ可以簡潔地處理數據集合。 4.常見錯誤包括空引用異常和索引超出範圍異常,調試技巧包括使用調試器和異常處理。 5.性能優化包括使用StringBuilder和避免不必要的裝箱和拆箱。

C#.NET應用的測試策略包括單元測試、集成測試和端到端測試。 1.單元測試確保代碼的最小單元獨立工作,使用MSTest、NUnit或xUnit框架。 2.集成測試驗證多個單元組合的功能,常用模擬數據和外部服務。 3.端到端測試模擬用戶完整操作流程,通常使用Selenium進行自動化測試。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

Atom編輯器mac版下載
最受歡迎的的開源編輯器

Dreamweaver CS6
視覺化網頁開發工具

Dreamweaver Mac版
視覺化網頁開發工具