减少HttpContext.Current的使用
大石头 authored at 2018-01-13 11:07:13
4.93 KiB
X
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Web;
using NewLife.Log;
using NewLife.Model;

namespace NewLife.Web
{
    /// <summary>全局错误处理模块</summary>
    public class ErrorModule : IHttpModule
    {
        #region IHttpModule Members
        void IHttpModule.Dispose() { }

        /// <summary>初始化模块,准备拦截请求。</summary>
        /// <param name="context"></param>
        void IHttpModule.Init(HttpApplication context)
        {
            context.Error += OnError;
        }
        #endregion

        #region 业务处理
        /// <summary>是否需要处理</summary>
        /// <param name="req"></param>
        /// <param name="ex"></param>
        /// <returns></returns>
        protected virtual Boolean NeedProcess(HttpRequest req, Exception ex)
        {
            if (ex is ThreadAbortException) return false;
            if (ex is CryptographicException && ex.Message.Contains("填充无效")) return false;

            // 文件不存在的异常只出现一次
            if (ex is HttpException && (ex.Message.Contains("文件不存在") || ex.Message.Contains("Not Found") || ex.Message.Contains("未找到路径")))
            {
                var url = req.RawUrl;
                if (!url.IsNullOrEmpty())
                {
                    if (fileErrors.Contains(url)) return false;
                    fileErrors.Add(url);
                }
            }
            // 无效操作,句柄未初始化,不用出现
            if (ex is InvalidOperationException && ex.Message.Contains("句柄未初始化")) return false;

            return true;
        }

        ICollection<String> fileErrors = new HashSet<String>(StringComparer.OrdinalIgnoreCase);

        /// <summary>错误处理方法</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void OnError(Object sender, EventArgs e)
        {
            //var ctx = HttpContext.Current;
            var ctx = (sender as HttpApplication).Context;
            var Server = ctx.Server;
            var Request = ctx.Request;
            var Response = ctx.Response;

            // 上层可能清空错误,这里不要拦截
            var ex = Server.GetLastError();
            //if (ex == null) return;

            if (!NeedProcess(Request, ex)) return;

            var sb = new StringBuilder();
            if (ex != null) sb.AppendLine(ex.ToString());
            if (!String.IsNullOrEmpty(Request.UserHostAddress))
                sb.AppendFormat("来源:{0}\r\n", Request.UserHostAddress);
            if (!String.IsNullOrEmpty(Request.UserAgent))
                sb.AppendFormat("平台:{0}\r\n", Request.UserAgent);
            sb.AppendFormat("文件:{0}\r\n", Request.CurrentExecutionFilePath);
            sb.AppendFormat("访问:{0}\r\n", Request.Url);
            if (!String.IsNullOrEmpty(Request.UrlReferrer + ""))
                sb.AppendFormat("引用:{0}\r\n", Request.UrlReferrer);
            if (Request.ContentLength > 0)
                sb.AppendFormat("方式:{0} {1:n0}\r\n", Request.HttpMethod, Request.ContentLength);
            else
                sb.AppendFormat("方式:{0}\r\n", Request.HttpMethod);

            var id = Thread.CurrentPrincipal;
            if (id != null && id.Identity != null && !String.IsNullOrEmpty(id.Identity.Name))
                sb.AppendFormat("用户:{0}({1})\r\n", id.Identity.Name, id.Identity.AuthenticationType);

            // 具体使用哪一种提供者由用户决定
            var eip = ObjectContainer.Current.Resolve<IErrorInfoProvider>();
            //var eip = ObjectContainer.Current.AutoRegister(typeof(IErrorInfoProvider)).Resolve<IErrorInfoProvider>();
            if (eip != null) eip.AddInfo(ex, sb);

            XTrace.WriteLine(sb.ToString());

            //OnErrorComplete();
        }

        ///// <summary>错误处理完成后执行。一般用于输出友好错误信息</summary>
        //protected virtual void OnErrorComplete()
        //{
        //    if (!XTrace.Debug)
        //    {
        //        var Server = HttpContext.Current.Server;
        //        var Response = HttpContext.Current.Response;

        //        Server.ClearError();
        //        Response.Write("非常抱歉,服务器遇到错误,请与管理员联系!");
        //    }
        //}
        #endregion
    }

    /// <summary>错误信息提供者。用于为错误处理模块提供扩展信息</summary>
    public interface IErrorInfoProvider
    {
        /// <summary>为指定错误添加附加错误信息。需要自己格式化并加换行</summary>
        /// <param name="ex"></param>
        /// <param name="builder"></param>
        void AddInfo(Exception ex, StringBuilder builder);
    }
}