간단한 ASPNET MVC 예외 처리 모듈

15259 단어
앞말
이상 처리는 모든 시스템에서 없어서는 안 될 중요한 부분이다. 이것은 우리 프로그램이 오류가 발생할 때 우호적으로 오류 정보를 알려주고 기록하게 할 수 있으며 더욱 중요한 것은 정상적인 데이터를 파괴하지 않고 시스템 운행에 영향을 주지 않도록 하는 것이다.이상 처리는 횡절점이어야 한다. 이른바 횡절점이란 각 부분을 모두 사용하는 것이다. 층의 어느 층이든 구체적인 업무 논리 모듈이든 주목하는 것은 똑같다.그래서 횡단 관심사는 한 곳에서 통일적으로 처리할 것입니다.MVC든 웹포름이든 이같은 실현을 제공해 이상을 집중적으로 처리할 수 있게 했다.
MVC에서 FilterConfig에서 기본적으로 HandleErrorAttribute를 등록했습니다. 이것은 필터입니다. 이것은 FilterAttribute 클래스를 계승하고 IExceptionFilter 인터페이스를 실현했습니다. 필터의 실행 과정에 관해서는 제 이전 글을 볼 수 있습니다.이상 처리를 하면 곧 500 오류 페이지, 기록 로그 등을 연상할 수 있고Handle Error Attribute는 쉽게 맞춤형 오류 페이지를 만들 수 있으며 기본적으로 Error 페이지입니다.로그 기록도 계승하고 Global Filter Collection에 등록하면 됩니다.Handle Error Attribute에 관해서는 많은 사람들이 어떻게 사용하는지 알고 있으니, 여기서는 소개하지 않겠습니다.
오케이, 본론으로 들어갑니다!MVC에서 이상을 처리하면 처음에 많은 사람들이Handle Error Attribute를 계승한 다음에 On Exception 방법을 다시 쓰고 자신의 논리를 추가한다고 믿는다. 예를 들어 이상 정보를 로그 파일에 쓰는 등이다.물론 이것은 타당하지 않지만 좋은 디자인은 장면이 구동하고 동적이며 배치할 수 있는 것이어야 한다.예를 들어 장면 중 하나에서 우리는 ExceptionA가 잘못된 페이지 A를 표시하기를 원했고 장면 2에서 우리는 그것이 잘못된 페이지 B를 표시하기를 원했다. 이곳의 장면은 프로젝트를 뛰어넘을 수도 있고 같은 시스템의 다른 모듈에 있을 수도 있다.또한 이상은 단계별로 나눌 수 있다. 예를 들어 ExceptionA가 발생했을 때 우리는 간단한 복구 상태만 있으면 프로그램이 계속 실행될 수 있다. ExceptionB가 발생했을 때 우리는 그것을 파일이나 시스템 로그에 기록하기를 원했고 ExceptionC가 발생했을 때 심각한 오류였다. 우리는 프로그램에 메일이나 문자 알림이 발생하기를 바란다.간단하게 말하면 서로 다른 장면은 서로 다른 수요를 가지고 우리의 프로그램은 변화에 더욱 잘 직면해야 한다.물론Handle Error Attribute를 계승하는 것도 위에서 말한 것을 충분히 실현할 수 있다. 단지 여기서 나는 그것을 확장하지 않고 모듈을 다시 작성하여 기존의Handle Error Attribute와 함께 사용할 수 있다.
2. 설계 및 실현
2.1 구성 정보 정의
위에서 우리가 해야 할 일을 알 수 있다. 서로 다른 이상에 대해, 우리는 그것의 처리 프로그램, 오류 페이지 등을 설정할 수 있기를 바란다.다음 구성 중 하나:
   <!--       -->
  <settingException>
    <exceptions>
      <!--add     group-->
      <add exception="Exceptions.PasswordErrorException" 
           view ="PasswordErrorView"
           handler="ExceptionHandlers.PasswordErrorExceptionHandler"/>
      <groups>
        <!--group         view handler-->
        <group view="EmptyErrorView" handler="ExceptionHandlers.EmptyExceptionHandler">
          <add exception="Exceptions.UserNameEmptyException"/>
          <add exception="Exceptions.EmailEmptyException"/>
        </group>        
      </groups>
    </exceptions>
  </settingException>

그 중에서 dd 노드는 구체적인 이상을 증가시키는 데 사용되며, exception 속성은 필수적이며,view는 오류 페이지를 나타내고,handler는 구체적인 처리 프로그램을 나타내며,view와handler가 없으면, 이상은 기본적인handleError Attribute 처리에 맡깁니다.그룹 노드는 그룹을 나누는 데 사용된다. 예를 들어 위의 UserNameEmptyException과 EmailEmptyException은 같은 처리 프로그램과 보기에 대응한다.
프로그램은 이 설정 정보를 반사적으로 읽고 해당하는 대상을 만들 것입니다.우리는 이 프로필을 웹에 두었다.config에서 수시로 변경할 수 있고 수시로 효력이 발생할 수 있습니다.
2.2 이상 정보 포장 대상
여기서 우리는 위의 노드에 대응하는 실체 대상을 정의한다.다음과 같습니다.
    public class ExceptionConfig
    {
        /// <summary>
        ///   
        /// </summary>
        public string View{get;set;}

        /// <summary>
        ///     
        /// </summary>
        public Exception Exception{get;set;}

        /// <summary>
        ///       
        /// </summary>
        public IExceptionHandler Handler{get;set;}
    }

2.3 Handler 인터페이스 정의
위에서 말한 바와 같이, 서로 다른 이상은 다른 처리 방식을 필요로 할 수 있다.여기서 우리는 다음과 같은 인터페이스를 설계했다.
    public interface IExceptionHandler
    {
        /// <summary>
        ///         
        /// </summary>
        bool HasHandled{get;set;}

        /// <summary>
        ///     
        /// </summary>
        /// <param name="ex"></param>
        void Handle(Exception ex);
    }

각종 이상 처리 프로그램은 이 인터페이스를 실현하기만 하면 된다.
2.3 IExceptionFilter 구현
이것은 필수적이다.다음은 IExceptionFilter 인터페이스를 실현하고 SettingExceptionProvider는 이상 대상 유형에 따라 설정 정보 (캐시) 에서 포장 대상을 가져옵니다.
    public class SettingHandleErrorFilter : IExceptionFilter
    {
        public void OnException(ExceptionContext filterContext)
        {
            if(filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }
            ExceptionConfig config = SettingExceptionProvider.Container[filterContext.Exception.GetType()];
            if(config == null)
            {
                return;
            }
            if(config.Handler != null)
            {
                //  Handle                  
                config.Handler.Handle(filterContext.Exception);
                if (config.Handler.HasHandled)
                {
                    //     ,       
                    filterContext.ExceptionHandled = true;
                    return;
                }
            }            
            //  ,       ,   
            if(!string.IsNullOrEmpty(config.View))
            {
                //          IView   
                ViewResult view = new ViewResult();
                view.ViewName = config.View;
                filterContext.Result = view;
                filterContext.ExceptionHandled = true;
                return;
            }
            //         
        }
    }

2.4 프로필 읽기, 비정상 정보 패키지 만들기
이 부분의 코드는 비교적 많은데, 사실상 웹을 읽고 있다는 것만 알면 된다.config의 사용자 정의 설정 노드만 있으면 됩니다.SettingExceptionProvider는 컨테이너 객체를 제공하는 데 사용됩니다.
    public class SettingExceptionProvider
    {
        public static Dictionary<Type, ExceptionConfig> Container =
            new Dictionary<Type, ExceptionConfig>();

        static SettingExceptionProvider()
        {
            InitContainer();
        }

        //      ,     
        private static void InitContainer()
        {
            var section = WebConfigurationManager.GetSection("settingException") as SettingExceptionSection;
            if(section == null)
            {
                return;
            }
            InitFromGroups(section.Exceptions.Groups);
            InitFromAddCollection(section.Exceptions.AddCollection);
        }

        private static void InitFromGroups(GroupCollection groups)
        {                      
            foreach (var group in groups.Cast<GroupElement>())
            {   
                ExceptionConfig config = new ExceptionConfig();
                config.View = group.View;
                config.Handler = CreateHandler(group.Handler);
                foreach(var item in group.AddCollection.Cast<AddElement>())
                {
                    Exception ex = CreateException(item.Exception);
                    config.Exception = ex;
                    Container[ex.GetType()] = config;
                }
            }
        }

        private static void InitFromAddCollection(AddCollection collection)
        {
            foreach(var item in collection.Cast<AddElement>())
            {
                ExceptionConfig config = new ExceptionConfig();
                config.View = item.View;
                config.Handler = CreateHandler(item.Handler);
                config.Exception = CreateException(item.Exception);
                Container[config.Exception.GetType()] = config;
            }
        }

        //         IExceptionHandler  
        private static IExceptionHandler CreateHandler(string fullName)             
        {
            if(string.IsNullOrEmpty(fullName))
            {
                return null;
            }
            Type type = Type.GetType(fullName);
            return Activator.CreateInstance(type) as IExceptionHandler;
        }

        //         Exception  
        private static Exception CreateException(string fullName)
        {
            if(string.IsNullOrEmpty(fullName))
            {
                return null;
            }
            Type type = Type.GetType(fullName);
            return Activator.CreateInstance(type) as Exception;
        }
    }

각 구성 노드에 대한 정보는 다음과 같습니다.
settingExceptions 노드:
    /// <summary>
    /// settingExceptions  
    /// </summary>
    public class SettingExceptionSection : ConfigurationSection 
    {
        [ConfigurationProperty("exceptions",IsRequired=true)]
        public ExceptionsElement Exceptions
        {
            get
            {
                return (ExceptionsElement)base["exceptions"];
            }
        }
    }

exceptions 노드:
    /// <summary>
    /// exceptions  
    /// </summary>
    public class ExceptionsElement : ConfigurationElement 
    {
        private static readonly ConfigurationProperty _addProperty =
            new ConfigurationProperty("", typeof(AddCollection), null, ConfigurationPropertyOptions.IsDefaultCollection);

        [ConfigurationProperty("", IsDefaultCollection = true)]
        public AddCollection AddCollection
        {
            get
            {
                return (AddCollection)base[_addProperty];
            }
        }

        [ConfigurationProperty("groups")]
        public GroupCollection Groups
        {
            get
            {
                return (GroupCollection)base["groups"];
            }
        }
    }

Group 노드 세트:
    /// <summary>
    /// group   
    /// </summary>
    [ConfigurationCollection(typeof(GroupElement),AddItemName="group")]
    public class GroupCollection : ConfigurationElementCollection
    {       
        /*override*/

        protected override ConfigurationElement CreateNewElement()
        {
            return new GroupElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return element;
        }
    }

그룹 노드:
    /// <summary>
    /// group  
    /// </summary>
    public class GroupElement : ConfigurationElement
    {
        private static readonly ConfigurationProperty _addProperty =
            new ConfigurationProperty("", typeof(AddCollection), null, ConfigurationPropertyOptions.IsDefaultCollection);

        [ConfigurationProperty("view")]
        public string View
        {
            get
            {
                return base["view"].ToString();
            }
        }

        [ConfigurationProperty("handler")]
        public string Handler
        {
            get
            {
                return base["handler"].ToString();
            }
        }

        [ConfigurationProperty("", IsDefaultCollection = true)]
        public AddCollection AddCollection
        {
            get
            {
                return (AddCollection)base[_addProperty];
            }
        }        
    }

add 노드 세트:
    /// <summary>
    /// add   
    /// </summary>    
    public class AddCollection : ConfigurationElementCollection 
    {          
        /*override*/

        protected override ConfigurationElement CreateNewElement()
        {
            return new AddElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return element;
        }
    }

add 노드:
    /// <summary>
    /// add  
    /// </summary>
    public class AddElement : ConfigurationElement
    {
        [ConfigurationProperty("view")]
        public string View
        {
            get
            {
                return base["view"] as string;
            }
        }

        [ConfigurationProperty("handler")]
        public string Handler
        {
            get
            {
                return base["handler"] as string;
            }
        }

        [ConfigurationProperty("exception", IsRequired = true)]
        public string Exception
        {
            get
            {
                return base["exception"] as string;
            }
        }
    }

 
테스트
OK, 다음 테스트를 해보겠습니다. 우선 FilterConfig의RegisterGlobalFilters 방법에서HandlerErrorAttribute 전에 필터를 등록하십시오.
  filters.Add(new SettingHandleErrorFilter()).
3.1 예외 개체 준비
몇 가지 간단한 예외 객체를 준비합니다.
 public class PasswordErrorException : Exception{}
 public class UserNameEmptyException : Exception{} 
 public class EmailEmptyException : Exception{}

3.2 준비Handler
위의 이상에 대해 우리는 두 개의handler를 준비했는데 하나는 비밀번호 오류 이상을 처리하고 하나는 빈 이상을 처리한다.여기에 실제 처리 코드가 없으니 구체적으로 어떻게 처리하는지는 구체적인 업무와 결합해야 한다.예:
    public class PasswordErrorExceptionHandler : IExceptionHandler
    {
        public bool HasHandled{get;set;}
        
        public void Handle(Exception ex)
        {
            //      ...
        }
    }

    public class EmptyExceptionHandler : IExceptionHandler
    {
        public bool HasHandled { get; set; }

        public void Handle(Exception ex)
        {
            //      ...
        }
    }

3.3 이상 던지기
위의 구성에 따라 Action에서 수동으로 throw 예외 발생
        public ActionResult Index()
        {
            throw new PasswordErrorException();
        }
        public ActionResult Index2()
        {
            throw new UserNameEmptyException();
        }
        public ActionResult Index3()
        {
            throw new EmailEmptyException();
        }

상응하는 Handler가 실행되고 브라우저에 우리가 설정한 오류 페이지가 나타나는 것을 볼 수 있습니다.
4. 총결산
사실 이것은 비교적 간단한 예일 뿐이기 때문에 나는 그것을 간단한 모듈이라고 부르고 프레임워크, 라이브러리 같은 단어를 사용한다.물론 우리는 실제 상황에 따라 그것을 확장하고 최적화할 수 있다.마이크로소프트 기업 라이브러리도 이런 모듈을 통합하고 있으니 관심 있는 분들은 알아보실 수 있습니다.
원본 다운로드

좋은 웹페이지 즐겨찾기