代码之家  ›  专栏  ›  技术社区  ›  Joseph Ferris

用于错误处理和丢失图像的httpmodule

  •  5
  • Joseph Ferris  · 技术社区  · 15 年前

    我有一个httpmodule,我把它组合在一起,将两个不同的在线源拼凑在一起,形成一个既适用于传统ASP.NET应用程序,也适用于ASP.NET MVC应用程序的模块。其中最大的部分来自于CodePlex上的Kigg项目。我的问题是处理404个错误,因为图像丢失。在下面的代码中,我必须显式地查找通过httpcontext的请求对象中的acceptedtypes集合请求的图像。如果我不进行检查,即使是丢失的图像也会导致重定向到web.config中我的部分中定义的404页。

    这种方法的问题在于(事实上它闻起来了),这只是为了图像。我基本上需要对每一种我不希望发生这种重定向行为的内容类型进行处理。

    看看下面的代码,是否有人建议进行某种重构,以便对非页面请求更为宽容?我仍然希望它们出现在IIS日志中(因此我可能必须删除clearror()调用),但我认为一个损坏的图像不会影响用户体验,从而将它们重定向到错误页面。

    代码如下:

    /// <summary>
    /// Provides a standardized mechanism for handling exceptions within a web application.
    /// </summary>
    public class ErrorHandlerModule : IHttpModule
    {
        #region Public Methods
    
        /// <summary>
        /// Disposes of the resources (other than memory) used by the module that implements 
        /// <see cref="T:System.Web.IHttpModule"/>.
        /// </summary>
        public void Dispose()
        {
        }
    
        /// <summary>
        /// Initializes a module and prepares it to handle requests.
        /// </summary>
        /// <param name="context">
        /// An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events 
        /// common to all application objects within an ASP.NET application.</param>
        public void Init(HttpApplication context)
        {
            context.Error += this.OnError;
        }
    
        #endregion
    
        /// <summary>
        /// Called when an error occurs within the application.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnError(object source, EventArgs e)
        {
            var httpContext = HttpContext.Current;
    
            var imageRequestTypes =
                httpContext.Request.AcceptTypes.Where(a => a.StartsWith("image/")).Select(a => a.Count());
    
            if (imageRequestTypes.Count() > 0)
            {
                httpContext.ClearError();
                return;
            }
    
            var lastException = HttpContext.Current.Server.GetLastError().GetBaseException();
            var httpException = lastException as HttpException;
            var statusCode = (int)HttpStatusCode.InternalServerError;
    
            if (httpException != null)
            {
                statusCode = httpException.GetHttpCode();
                if ((statusCode != (int)HttpStatusCode.NotFound) && (statusCode != (int)HttpStatusCode.ServiceUnavailable))
                {
                    // TODO: Log exception from here.
                }
            }
    
            var redirectUrl = string.Empty;
    
            if (httpContext.IsCustomErrorEnabled)
            {
                var errorsSection = WebConfigurationManager.GetSection("system.web/customErrors") as CustomErrorsSection;
                if (errorsSection != null)
                {
                    redirectUrl = errorsSection.DefaultRedirect;
    
                    if (httpException != null && errorsSection.Errors.Count > 0)
                    {
                        var item = errorsSection.Errors[statusCode.ToString()];
    
                        if (item != null)
                        {
                            redirectUrl = item.Redirect;
                        }
                    }
                }
            }
    
            httpContext.Response.Clear();
            httpContext.Response.StatusCode = statusCode;
            httpContext.Response.TrySkipIisCustomErrors = true;
            httpContext.ClearError();
    
            if (!string.IsNullOrEmpty(redirectUrl))
            {
                var mvcHandler = httpContext.CurrentHandler as MvcHandler;
                if (mvcHandler == null)
                {
                    httpContext.Server.Transfer(redirectUrl);                    
                }
                else
                {
                    var uriBuilder = new UriBuilder(
                        httpContext.Request.Url.Scheme, 
                        httpContext.Request.Url.Host, 
                        httpContext.Request.Url.Port, 
                        httpContext.Request.ApplicationPath);
    
                    uriBuilder.Path += redirectUrl;
    
                    string path = httpContext.Server.UrlDecode(uriBuilder.Uri.PathAndQuery);
                    HttpContext.Current.RewritePath(path, false);
                    IHttpHandler httpHandler = new MvcHttpHandler();
    
                    httpHandler.ProcessRequest(HttpContext.Current);
                }
            }
        }
    }
    

    如有任何反馈,我们将不胜感激。我目前正在使用的应用程序是一个ASP.NET MVC应用程序,但正如我提到的,它是为使用MVC处理程序而编写的,但仅当当前处理程序是该类型时。

    编辑: 我忘了提一下这个例子中的“hack”是onerror()中的以下几行:

            var imageRequestTypes =
            httpContext.Request.AcceptTypes.Where(a => a.StartsWith("image/")).Select(a => a.Count());
    
        if (imageRequestTypes.Count() > 0)
        {
            httpContext.ClearError();
            return;
        }
    
    3 回复  |  直到 15 年前
        1
  •  5
  •   Joseph Ferris    15 年前

    最终,问题是由于没有区分传统ASP.NET应用程序和ASP.NET MVC应用程序提供的不同类型的上下文。通过提供一个检查来确定我正在处理的上下文类型,我能够做出相应的响应。

    我为httptransfer和mvctransfer添加了单独的方法,允许我重定向到错误页面,特别是在需要时。我还改变了周围的逻辑,这样我就可以很容易地在本地和开发机器上获得我的ysod,而不需要处理程序接受异常。

    除了用于将异常记录到数据库的代码(由todo注释表示)之外,我们使用的最终代码是:

    using System;
    using System.Net;
    using System.Security.Principal;
    using System.Web;
    using System.Web.Configuration;
    using System.Web.Mvc;
    
    using Diagnostics;
    
    /// <summary>
    /// Provides a standardized mechanism for handling exceptions within a web application.
    /// </summary>
    public sealed class ErrorHandlerModule : IHttpModule
    {
        #region Public Methods
    
        /// <summary>
        /// Disposes of the resources (other than memory) used by the module that implements 
        /// <see cref="T:System.Web.IHttpModule"/>.
        /// </summary>
        public void Dispose()
        {
        }
    
        /// <summary>
        /// Initializes a module and prepares it to handle requests.
        /// </summary>
        /// <param name="context">
        /// An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events 
        /// common to all application objects within an ASP.NET application.</param>
        public void Init(HttpApplication context)
        {
            context.Error += OnError;
        }
    
        #endregion
    
        #region Private Static Methods
    
        /// <summary>
        /// Performs a Transfer for an MVC request.
        /// </summary>
        /// <param name="url">The URL to transfer to.</param>
        /// <param name="currentContext">The current context.</param>
        private static void HttpTransfer(string url, HttpContext currentContext)
        {
            currentContext.Server.TransferRequest(url);
        }
    
        /// <summary>
        /// Performs a Transfer for an MVC request.
        /// </summary>
        /// <param name="url">The URL to transfer to.</param>
        /// <param name="currentContext">The current context.</param>
        private static void MvcTransfer(string url, HttpContext currentContext)
        {
            var uriBuilder = new UriBuilder(
                currentContext.Request.Url.Scheme,
                currentContext.Request.Url.Host,
                currentContext.Request.Url.Port,
                currentContext.Request.ApplicationPath);
    
            uriBuilder.Path += url;
    
            string path = currentContext.Server.UrlDecode(uriBuilder.Uri.PathAndQuery);
            HttpContext.Current.RewritePath(path, false);
            IHttpHandler httpHandler = new MvcHttpHandler();
    
            httpHandler.ProcessRequest(HttpContext.Current);
        }
    
        #endregion
    
        #region Private Methods
    
        /// <summary>
        /// Called when an error occurs within the application.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private static void OnError(object source, EventArgs e)
        {
            var httpContext = HttpContext.Current;
            var lastException = HttpContext.Current.Server.GetLastError().GetBaseException();
            var httpException = lastException as HttpException;
            var statusCode = (int)HttpStatusCode.InternalServerError;
    
            if (httpException != null)
            {
                if (httpException.Message == "File does not exist.")
                {
                    httpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
                    httpContext.ClearError();
                    return;
                }
    
                statusCode = httpException.GetHttpCode();
            }
    
            if ((statusCode != (int)HttpStatusCode.NotFound) && (statusCode != (int)HttpStatusCode.ServiceUnavailable))
            {
                // TODO : Your error logging code here.
            }
    
            var redirectUrl = string.Empty;
    
            if (!httpContext.IsCustomErrorEnabled)
            {
                return;
            }
    
            var errorsSection = WebConfigurationManager.GetSection("system.web/customErrors") as CustomErrorsSection;
            if (errorsSection != null)
            {
                redirectUrl = errorsSection.DefaultRedirect;
    
                if (httpException != null && errorsSection.Errors.Count > 0)
                {
                    var item = errorsSection.Errors[statusCode.ToString()];
    
                    if (item != null)
                    {
                        redirectUrl = item.Redirect;
                    }
                }
            }
    
            httpContext.Response.Clear();
            httpContext.Response.StatusCode = statusCode;
            httpContext.Response.TrySkipIisCustomErrors = true;
            httpContext.ClearError();
    
            if (!string.IsNullOrEmpty(redirectUrl))
            {
                var mvcHandler = httpContext.CurrentHandler as MvcHandler;
                if (mvcHandler == null)
                {
                    try
                    {
                        HttpTransfer(redirectUrl, httpContext);
                    }
                    catch (InvalidOperationException)
                    {
                        MvcTransfer(redirectUrl, httpContext);
                    }
                }
                else
                {
                    MvcTransfer(redirectUrl, httpContext);
                }
            }
        }
    
        #endregion
    }
    
        2
  •  0
  •   Anthony Johnston    15 年前

    为什么不在你的global.asax中找到404?

    protected void Application_Error(object sender, EventArgs args) {
    
        var ex = Server.GetLastError() as HttpException;
        if (ex != null && ex.ErrorCode == -2147467259) {
    
        }
    }
    
        3
  •  0
  •   Neal    15 年前

    如果我理解正确,您只想为导致404的操作处理错误?

    您可以检查请求的路由是否为空或停止路由-这基本上是URL路由处理程序的工作方式,以决定请求是否应继续进入MVC管道。

    var iHttpContext = new HttpContextWrapper( httpContext );
    var routeData = RouteTable.Routes.GetRouteData( iHttpContext );
    if( routeData == null || routeData.RouteHandler is StopRoute )
    {
      // This is a route that would not normally be handled by the MVC pipeline
      httpContext.ClearError();
      return;
    }
    

    另外,由于404的重定向会导致用户体验不理想,这是ASP.NET的遗留问题(在这里,您无法将视图处理与请求处理分开)。管理404的正确方法是将404状态代码返回到浏览器并显示自定义错误页,而不是重定向(这将导致302状态代码发送到浏览器)。