Summary of how ASP. NET MVC resolves exception handling

Source: Internet
Author: User
Tags define exception
ASP. NET MVC is a highly extensible development framework in which I will implement integration with EntLib through its extensions and provide a complete solution for exception handling.

EntLib's Exception Handling Application Block (Exception Handling Application Block) is a good exception handling framework that allows us to define exception handling policies in a configuration-based manner. While ASP. NET MVC is an extensible development framework, in this article I will implement integration with EntLib through its extensions and provide a complete solution for exception handling.

First, the basic exception handling strategy

Let's start with a discussion of the exception handling strategy that our solution specifically uses:

For an exception that is thrown by an action method that executes a controller, we process it according to the specified configuration policy. We can take logging, exception substitution and encapsulation of these common exception handling methods;

For a treated exception, if the exception handling policy specifies that it needs to be thrown, it is automatically redirected to the error page that matches the exception type. We will maintain a matching relationship between the exception type and the error view;

For a treated exception, if the exception handling policy specifies that it does not need to be thrown, the error-handling action that matches the current action action is executed for processing. The exception handling action method defaults to a naming convention such as "On{action}error", and the current context is bound to the parameters of the exception handling action method. In addition to the other times, we will set the current modelstate error message;

If the user has not defined the appropriate exception handling action, the error page redirection method is still used for exception handling.

II. handling exceptions through custom action

To give the reader a deep understanding of the exception handling page described above, let's take an example demonstration. This instance is used to impersonate a user login, and we define the following Model:logininfomodel, which contains only the user name and password two attributes.


  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;}    }  }

We define the following AccountController, which is a subclass of our custom Basecontroller. AccountController call the base class constructor when constructing the specified parameter represents the configuration name of the exception handling policy. The Signin method represents an operation for "login", while Onsigninerror represents the exception handling action for that operation. If the exception thrown in the signin operation does not have to be thrown after processing, it is called by Onsigninerror, and the modelstate has been set to the appropriate error message.


 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}); }  }

The authentication logic defined in the Signin operation method is this: If the user name is not "Foo" then the invalidusernameexception exception is thrown; If the password is not "password" The usernamepasswordnotmatchexception exception is thrown. Here is the definition of the view that corresponds to the signin operation:


  @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"/>  }

The exception handling policy specified in AccountController initialization "MyPolicy" is defined in the following configuration. We deal specifically with Invalidusernameexception and usernamepasswordnotmatchexception that are thrown by signin operation Methods, And Errormessagesettinghandler is our custom exception handler, which is only used to set the error message. If the above two types of exceptions are thrown, the final error message will be specified as "User name does not exist!" as shown in the following code fragment and "User name does not match password!".


  <exceptionHandling> <exceptionPolicies> <add name= "MyPolicy" > <exceptionTypes> <add name= "invalidusernameexception" type= "Artech.Mvc.ExceptionHandling.Models.InvalidUserNameException, Art Ech. Mvc.exceptionhandling "posthandlingaction=" None "> <exceptionHandlers> <add name=" Errorm Essagesettinghandler "Type=" Artech.Mvc.ExceptionHandling.ErrorMessageSettingHandler, Artech.Mvc.ExceptionHandlin G "errormessage=" User name does not exist! " /> </exceptionHandlers> </add> <add name= "usernamepasswordnotmatchexception" t         Ype= "Artech.Mvc.ExceptionHandling.Models.UserNamePasswordNotMatchException, Artech.Mvc.ExceptionHandling" posthandlingaction= "None" > <exceptionHandlers> <add name= "Errormessagesettinghandler" t      Ype= "Artech.Mvc.ExceptionHandling.ErrorMessageSettingHandler, Artech.Mvc.ExceptionHandling"     Errormessage= "User name does not match password!" /> </exceptionHandlers> </add> </exceptionTypes> </add> </exception Policies> </exceptionHandling>

Now that we have set AccountController and sign as the default controller and action through route mapping, our application opens. The appropriate error message will be automatically obtained in the ValidationSummary if the wrong user name and error codes are entered.

III. handling exceptions by configuring the error view

In the previous configuration, the configuration policy for both exception types Invalidusernameexception and Usernamepasswordnotmatchexception set the Posthandlingaction property to "None "means that the original exception and the treated exception will not be re-thrown. Now we set this property to "thrownewexception", which means that we will re-throw the unhandled exception.


  <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>

According to our exception handling strategy above, in this case we will use the "error page" way to do exception handling. Also Handleerrorattribute is handled similarly, we support the matching relationship between the exception type and the error view, which is defined by a configuration similar to the following. It is worth mentioning that the exception type here is treated and re-thrown after the exception.


  <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>

As shown in the previous configuration, we have defined different error View for both the invalidusernameexception and usernamepasswordnotmatchexception exception types, namely " Invalideusernameerror "and" usernamepasswordnotmatcherror ", detailed definitions are as follows:


  @{     Layout = null;   }   <! DOCTYPE html>   

Now we run our program in the way above and will automatically display the corresponding error page when we enter the wrong username and password separately.

Iv. Custom Actioninvoker:exceptionactioninvoker

The two different types of exception handling described above are eventually implemented by custom Actioninvoker, which we name as Exceptionactioninvoker. As the code snippet below, Exceptionactioninvoker directly inherits from Controlleractioninvoker. The Exceptionpolicy property is a Exceptionpolicyimpl object that is created based on the specified exception policy name for exception handling for EntLib. The property Geterrorview is a delegate that is used to obtain the Viewresult object as the error page. The core definition of the entire exception handling in the Invokeaction method, the Handleerroractionname parameter specified in the method represents the "Exception handling operation name", the entire method is implemented according to the above exception handling policy.


  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 {Protect      Ed 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 Exceptioncontex        T! "," ControllerContext ");          } try {exceptioncontext.exceptionhandled = true; if (this. Exceptionpolicy.handleexception (exceptioncontext.exception)) {handlerethrownexception (ExceptionContex          T); } else {if (ExceptionHandlingContext.Current.Errors.Count = = 0) {Ex            CEPTIONHANDLINGCONTEXT.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 error ViewName = 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 = Exceptionha Ndlingsettings.exceptionerrorviews. Cast<exceptionerrorviewelement> (). FirstOrDefault (El=>el.        Exceptiontype = = Exceptiontype); if (null! = Element) {return element.        Errorview; } if (null== element && null! = exceptiontype.basetype!= null) {return geterrorviewname (exc        Eptiontype.basetype);        } else {return "Error"; }      }    }  }

Five, custom Controller:basecontroller

The Exceptionactioninvoker is eventually called in our custom controller base class Basecontroller. The Exceptionactioninvoker object is initialized in the constructor and is called in the overridden Onexception method.


  Using System;   Using SYSTEM.WEB.MVC; Namespace Artech.Mvc.ExceptionHandling {public abstract class Basecontroller Controller {public Basecont Roller (string exceptionpolicy) {func<string, handleerrorinfo, viewresult> Geterrorview = (ViewName, h Andleerrorinfo) = = 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 (Exceptionhandlingcontex Tscope 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); }        }      }    }  }

It is worth mentioning that the entire Onexception method is done in a exceptionhandlingcontextscope. As the name implies, we created a range for exceptionhandlingcontext through Exceptionhandlingcontextscope. Exceptionhandlingcontext is defined as follows, and we can get the current exceptioncontext and modelerrorcollection through it, The static property, current, returns the currently Exceptionhandlingcontext object.


  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;}    }  }

In the Onexception method of Basecontroller, when the invokeaction of Exceptionactioninvoker is executed, We will transfer the current Exceptionhandlingcontext modelerror to the current modelstate. This is why we will display the error message through ValidationSummary. For our example, the error message is specified by the errormessagesettinghandler shown below, and it simply adds the specified error message to the current Exceptionhandlingcontext Errors property collection.


 [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 = = Exceptionhandli      Ngcontextcurrent) {throw new InvalidOperationException (""); } if (Stringisnullorempty (thiserrormessage)) {Exceptionhandlingcontextcurrenterrorsadd (ExceptionMessa      GE);      } else {exceptionhandlingcontextcurrenterrorsadd (thiserrormessage);    } return exception; }  }
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.