Asp.net 用于错误处理和缺少映像的HttpModule

Asp.net 用于错误处理和缺少映像的HttpModule,asp.net,asp.net-mvc,error-handling,http-status-code-404,httpmodule,Asp.net,Asp.net Mvc,Error Handling,Http Status Code 404,Httpmodule,我有一个HttpModule,我把两个不同的在线资源拼凑在一起,使之(大部分)既适用于传统的ASP.NET应用程序,也适用于ASP.NET MVC应用程序。其中最大的部分来自CodePlex上的kigg项目。我的问题是处理404错误,因为缺少图像。在下面的代码中,我必须在HttpContext的请求对象中通过AcceptedTypes集合显式查找正在请求的图像。如果我不进行此检查,即使缺少图像也会导致重定向到Web.config中我的部分中定义的404页面 这种方法的问题在于(除了气味之外)这只

我有一个HttpModule,我把两个不同的在线资源拼凑在一起,使之(大部分)既适用于传统的ASP.NET应用程序,也适用于ASP.NET MVC应用程序。其中最大的部分来自CodePlex上的kigg项目。我的问题是处理404错误,因为缺少图像。在下面的代码中,我必须在HttpContext的请求对象中通过AcceptedTypes集合显式查找正在请求的图像。如果我不进行此检查,即使缺少图像也会导致重定向到Web.config中我的部分中定义的404页面

这种方法的问题在于(除了气味之外)这只是针对图像。我基本上必须对我不希望发生这种重定向行为的每一种可以想象的内容类型都这样做

看看下面的代码,有人能推荐一些重构方法,让它对非页面请求更加宽松吗?我仍然希望它们出现在IIS日志中(因此我可能必须删除ClearError()调用),但我不认为损坏的图像会影响用户体验,从而将它们重定向到错误页面

守则如下:

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

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

protected void Application_Error(object sender, EventArgs args) {

    var ex = Server.GetLastError() as HttpException;
    if (ex != null && ex.ErrorCode == -2147467259) {

    }
}

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

您可以检查请求的路由是否为null或stop routed-这是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的遗留问题(在ASP.NET中,您无法将视图处理与请求处理分开)。管理404的正确方法是将404状态代码返回浏览器并显示自定义错误页面,而不是重定向(这将导致302状态代码发送到浏览器)。

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

我为HttpTransfer和MVCtrTransfer添加了单独的方法,允许我重定向到错误页面,特别是在需要时。我还改变了逻辑,这样我就可以轻松地在本地和开发机器上获得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
}
使用系统;
Net系统;
使用System.Security.Principal;
使用System.Web;
使用System.Web.Configuration;
使用System.Web.Mvc;
使用诊断;
/// 
///提供用于处理web应用程序内异常的标准化机制。
/// 
公共密封类ErrorHandlerModule:IHttpModule
{
#区域公共方法
/// 
///处置由实现的模块使用的资源(内存除外)
/// .
/// 
公共空间处置()
{
}
/// 
///初始化模块并使其准备好处理请求。
/// 
/// 
///提供对方法、属性和事件的访问的
///ASP.NET应用程序中的所有应用程序对象通用。
公共void Init(HttpApplication上下文)
{
context.Error+=OnError;
}
#端区
#区域私有静态方法
/// 
///对MVC请求执行传输。
/// 
///要传输到的URL。
///当前环境。
私有静态无效HttpTransfer(字符串url,HttpContext-currentContext)
{
currentContext.Server.TransferRequest(url);
}
/// 
///对MVC请求执行传输。
/// 
///要传输到的URL。
///当前环境。
私有静态void MvcTransfer(字符串url,HttpContext-currentContext)
{
var uriBuilder=新的uriBuilder(
currentContext.Request.Url.Scheme,
currentContext.Request.Url.Host,
currentContext.Request.Url.Port,
currentContext.Request.ApplicationPath);
uriBuilder.Path+=url;
字符串路径=currentContext.Server.UrlDecode(uriBuilder.Uri.PathAndQuery);
HttpContext.Current.RewritePath(路径,false);
IHttpHandler httpHandler=新的MVHttpHandler();
ProcessRequest(HttpContext.Current);
}
#端区
#区域私有方法
/// 
///当应用程序中发生错误时调用。
/// 
///消息来源。
///包含事件数据的实例。
私有静态void OnError(对象源、事件参数e)
{
var httpContext=httpContext.Current;
var lastException=HttpContext.Current.Server.GetLastError().GetBaseException();
var httpException=lastException作为httpException;
var statusCode=(int)HttpStatusCode.InternalServerError;
if(httpException!=null)
{
if(httpException.Message==“文件不存在。”)
{
httpContext.Response.StatusCode=(int)HttpStatusCode.NotFound;
httpContext.ClearError();
返回;
}
statusCode=httpException.GetHttpCode();
}
if((statusCode!=(int)HttpStatusCode.NotFound)和&(statusCode!=(int)HttpStatusCode.ServiceUnavailable))
{
//TODO:在此处记录错误代码。
}
var redirectUrl=string.Empty;
如果(!httpContext.IsCustomErrorEnabled)
{
返回;
}
var errorsSection=WebConfiguration Manager.GetSection(“system.web/customErrors”)作为CustomErrorsSection;
我
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
}