Maison  >  Article  >  développement back-end  >  Résumé des solutions ASP.NET MVC pour la gestion des exceptions

Résumé des solutions ASP.NET MVC pour la gestion des exceptions

巴扎黑
巴扎黑original
2017-08-16 16:16:361495parcourir

ASP.NET MVC est un framework de développement très extensible. Dans cet article, je vais implémenter l'intégration avec EntLib via son extension et fournir une solution complète de gestion des exceptions.

Le bloc d'application de gestion des exceptions d'EntLib est un bon cadre de gestion des exceptions, qui nous permet de définir des stratégies de gestion des exceptions via la configuration. ASP.NET MVC est un framework de développement très extensible. Dans cet article, je vais l'intégrer à EntLib via son extension et fournir une solution complète de gestion des exceptions.

1. Stratégie de gestion des exceptions de base

Discutons d'abord de la stratégie spécifique de gestion des exceptions adoptée par notre solution :

Pour les exceptions levées par l'exécution d'une méthode Action du contrôleur, nous les traiterons selon la stratégie de configuration spécifiée. Nous pouvons adopter des méthodes courantes de gestion des exceptions telles que la journalisation, le remplacement des exceptions et l'encapsulation

Pour les exceptions gérées, si la politique de gestion des exceptions stipule qu'elles doivent être levées, elles seront automatiquement redirigées vers la page d'erreur ; type d'exception correspondant. Nous maintiendrons une relation de correspondance entre le type d'exception et la vue des erreurs ;

Pour l'exception gérée, si la politique de gestion des exceptions stipule qu'elle n'a pas besoin d'être lancée, une opération correspondant à l'action actuelle sera exécuté. Gestion des erreurs L’action est utilisée pour gérer l’erreur. La méthode Action de gestion des exceptions utilise la règle de dénomination « On{Action}Error » par défaut, et le contexte actuel sera lié aux paramètres de la méthode d'action de gestion des exceptions. De plus, nous définirons les informations d'erreur du ModelState actuel ;

Si l'utilisateur n'a pas défini l'action de gestion des exceptions correspondante, la méthode "redirection de page d'erreur" sera toujours utilisée pour la gestion des exceptions.

2. Gestion des exceptions via des actions personnalisées

Afin de donner aux lecteurs une compréhension approfondie de la page de gestion des exceptions présentée ci-dessus, effectuons une exemple de démonstration. Cette instance est utilisée pour simuler la connexion de l'utilisateur. Nous définissons le modèle suivant qui ne contient que deux attributs : nom d'utilisateur et mot de passe : 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; }
    }
  }

Nous définissons le AccountController suivant, qui est une sous-classe de notre BaseController personnalisé. Lorsque AccountController appelle le constructeur de classe de base pendant la construction, les paramètres spécifiés représentent le nom de configuration de la stratégie de gestion des exceptions. La méthode SignIn représente l'opération « login » et OnSignInError représente l'opération de gestion des exceptions correspondant à l'opération. Si l'exception levée lors de l'opération SignIn est gérée et n'a plus besoin d'être levée, OnSignInError sera appelé et ModelState a été défini avec le message d'erreur correspondant.


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

La logique d'authentification spécifiquement définie dans la méthode d'opération SignIn est la suivante : si le nom d'utilisateur n'est pas "Foo", une InvalidUserNameException sera levée si le ; le mot de passe n'est pas "mot de passe" » lève une exception UserNamePasswordNotMatchException. Voici la définition de la View correspondant à l'opération SignIn :


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

La politique de gestion des exceptions "myPolicy" spécifiée lors de l'initialisation du AccountController est définie dans ce qui suit configuration. Nous traitons spécifiquement les exceptions InvalidUserNameException et UserNamePasswordNotMatchException levées par la méthode d'opération SignIn, et ErrorMessageSettingHandler est notre gestionnaire d'exceptions personnalisé, qui est uniquement utilisé pour définir le message d'erreur. Comme le montre l'extrait de code suivant, si les deux types d'exceptions ci-dessus sont générés, les messages d'erreur finaux seront spécifiés comme « Le nom d'utilisateur n'existe pas ! » et « Le nom d'utilisateur ne correspond pas au mot de passe ! ».


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

Maintenant, nous définissons AccountController et Sign comme contrôleur et action par défaut via le mappage d'itinéraire et démarrons notre application. Si vous entrez un nom d'utilisateur et un mot de passe incorrects, vous recevrez automatiquement le message d'erreur correspondant dans le ValidationSummary.

3. Gestion des exceptions via la vue des erreurs configurée

Dans la configuration ci-dessus, pour les deux exceptions InvalidUserNameException et UserNamePasswordNotMatchException La configuration Une stratégie de ce type définit l'attribut PostHandlingAction sur "Aucun", ce qui signifie que l'exception d'origine et l'exception gérée ne seront pas renvoyées. Nous définissons maintenant cette propriété sur "ThrowNewException", ce qui signifie que nous allons relancer l'exception gérée.


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

Selon notre stratégie de gestion des exceptions ci-dessus, dans ce cas, nous utiliserons la méthode "page d'erreur" pour la gestion des exceptions. HandleErrorAttribute est également géré de la même manière. Nous prenons en charge la relation de correspondance entre les types d'exception et les vues d'erreur, qui sont définies via une configuration similaire à celle suivante. Il convient de mentionner que le type d'exception ici est une exception qui est renvoyée après traitement.


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

Comme indiqué dans la configuration ci-dessus, nous avons défini différentes vues d'erreurs pour les deux types d'exceptions InvalidUserNameException et UserNamePasswordNotMatchException, à savoir "InvalideUserNameError" et "UserNamePasswordNotMatchError", la définition détaillée est la suivante :


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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn