ASP.NET 2.0:编写错误页面的最佳实践

时间:2009-08-04 13:46:45

标签: asp.net error-handling global-asax

在asp.net 2.0网站上,编写错误页面的最佳方法是什么。我在以下位置看过以下部分:

  • 的Web.Config

    <customErrors mode="RemoteOnly" defaultRedirect="~/Pages/Common/DefaultRedirectErrorPage.aspx">
    

  • 的Global.asax

    void Application_Error(object sender, EventArgs e) 
    { 
    }
    

  • 我没有得到如何以最佳方式使用它们来进行错误处理。

    请指导我最好的方法。

    2 个答案:

    答案 0 :(得分:9)

    在我的全局asax中,我总是检查它是什么类型的http错误......

    然后转移到web.config中指定的正确错误页面 我喜欢处理通常的嫌疑人,404(丢失页面)和500(服务器错误)

    http状态代码的一些背景是重要的,以了解它们的处理原因:

    http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

    我的web.config看起来像这样

    <customErrors mode="On"  defaultRedirect="~/error.aspx"  >
      <error statusCode="404" redirect="~/lost.aspx"  />
      <error statusCode="500" redirect="~/error.aspx"  />
    </customErrors>
    

    我丢失的页面中有逻辑,试图找到他们可能一直在寻找的页面的链接,以及其他一些格式。

    我的错误页面略有不同,显示了一些错误消息,

    所以我的处理方式不同。

    取决于您是否拥有网站的安全区域,您可能需要处理401/403?

    protected void Application_Error(object sender, EventArgs e)
    {
        var context = Context;
    
    
        var error = context.Server.GetLastError() as HttpException;
        var statusCode = error.GetHttpCode().ToString();
    
        // we can still use the web.config custom errors information to
        // decide whether to redirect
        var config = (CustomErrorsSection)WebConfigurationManager.GetSection("system.web/customErrors");
        if (config.Mode == CustomErrorsMode.On ||
            (config.Mode == CustomErrorsMode.RemoteOnly && context.Request.Url.Host != "localhost"))
        {
            // set the response status code
            context.Response.StatusCode = error.GetHttpCode();
    
            // Server.Transfer to correct ASPX file for error
            if (config.Errors[statusCode] != null)
            {
    
                HttpContext.Current.Server.Transfer(config.Errors[statusCode].Redirect);
            }
            else
                HttpContext.Current.Server.Transfer(config.DefaultRedirect);
        }
    }
    

    我服务器转移的原因是搜索引擎不会混淆,并保持我的网站管理员日志有意义...如果你重定向你返回一个http状态302告诉浏览器转到重定向到页面..然后这个下一页返回状态代码200(ok)。

    302 - &gt; 200,或甚至302 - > 404有不同的含义,只有404 ......

    然后说我的404错误页面我确保我设置了http错误的状态代码:

    protected void Page_PreRender(object sender, EventArgs e)
    {
        Response.Status = "404 Lost";
        Response.StatusCode = 404;
    
    }
    

    这篇文章对我有帮助,我知道我想做什么,但我喜欢这段代码看web.config设置... http://helephant.com/2009/02/improving-the-way-aspnet-handles-404-requests/

      

    返回正确的状态代码

         

    默认处理404的页面   错误页面不会返回404状态   代码到浏览器。它显示了   您提供给的错误消息   用户但没有任何额外的   将页面标记为的信息   错误页面。

         

    这称为软404.软404   页面不如那些页面好   返回404状态代码,因为   返回404状态代码让   访问你的文件的任何东西   页面是一个错误页面而不是一个   你站点的真实页面。这主要是   因为那时对搜索引擎很有用   他们知道他们应该去世   他们的索引中的页面,所以用户不会   关注您网站的死链接   结果页。

         

    返回404状态代码的页面是   也适用于错误检测   因为它们会记录在你的   服务器日志,如果你有意外   404错误,它们很容易找到。   这是404错误的一个例子   Google网站站长工具中的报告:

    <强> EDITS

      

    需要写吗?   global.asax中的server.clearerror()?   它有什么影响

    • 不,您可以在错误页面上执行此操作, 不确定影响?如果你做了 转移非,如果你重定向,那里 可能是另一种可能性 请求之间发生错误? 我不知道
      

    为什么在web.config中我们应该写error.aspx   两次,状态码为500,另一次是   defaultredirect

    • 我使用2因为丢失的页面应该 显示/并做一些不同的事情 服务器错误。错误页面显示 用户有一个错误,我们 无法从...和它恢复 可能是我们的错。我留下了 任何其他的默认重定向 错误代码也是如此。 403,401,400( 他们更罕见,但应该是 已处理)
      

    你能告诉我error.aspx和lost.aspx的代码。

    • 这取决于网站的类型 你有。你得到的错误是一样的 方式,但你用它做什么是最好的 您。在我丢失的页面上,我搜索 用户可能已经有的一些内容 寻找。我记录的错误页面 错误,所以用户友好的oops 页面......你需要弄清楚 需要什么。

    答案 1 :(得分:3)

    BigBlondeViking的回复对我很有帮助,除了我发现它没有处理403(当你尝试直接访问/ Scripts /或/ Content /目录时ASP会生成。)看来这并不是作为例外推出的。因此在Application_Error处理中不可捕获。 (这被外部公司认定为“安全漏洞” - 不要让我开始这样做!)

    protected void Application_PostRequestHandlerExecute(object sender, EventArgs e)
    {
        if (!Context.Items.Contains("HasHandledAnError")) // have we alread processed?
        {
            if (Response.StatusCode > 400 &&  // any error
                Response.StatusCode != 401)   // raised when login is required
            {
                Exception exception = Server.GetLastError();    // this is null if an ASP error
                if (exception == null)
                {
                    exception = new HttpException((int)Response.StatusCode, HttpWorkerRequest.GetStatusDescription(Response.StatusCode));
                }
                HandleRequestError(exception); // code shared with Application_Error
            }
        }
    }
    

    我在常见的错误处理方面做了一些小改动。当我们使用ASP.NET MVC时,我想显式调用控制器,并传递异常对象。这允许我访问异常本身,以便我可以根据代码记录/发送详细的电子邮件;

    public ActionResult ServerError(Exception exception)
    {
        HttpException httpException = exception as HttpException;
        if(httpException != null)
        {
            switch (httpException.GetHttpCode())
            {
                case 403:
                case 404:
                    Response.StatusCode = 404;
                    break;
            }
            // no email...
            return View("HttpError", httpException);
        }
        SendExceptionMail(exception);
        Response.StatusCode = 500;
        return View("ServerError", exception);
    }
    

    为了传递异常OBJECT(不仅仅是消息和代码),我明确地调用了控制器:

    protected void HandleRequestError(Exception exception)
    {
        if (Context.Items.Contains("HasHandledAnError"))
        {
            // already processed
            return;
        }
        // mark as processed.
        this.Context.Items.Add("HasHandledAnError", true);
    
        CustomErrorsSection customErrorsSection = WebConfigurationManager.GetWebApplicationSection("system.web/customErrors") as CustomErrorsSection;
    
        // Do not show the custom errors if
        // a) CustomErrors mode == "off" or not set.
        // b) Mode == RemoteOnly and we are on our local development machine.
        if (customErrorsSection == null || !Context.IsCustomErrorEnabled ||
            (customErrorsSection.Mode == CustomErrorsMode.RemoteOnly && Request.IsLocal))
        {
            return;
        }
    
        int httpStatusCode = 500;   // by default.
        HttpException httpException = exception as HttpException;
        if (httpException != null)
        {
            httpStatusCode = httpException.GetHttpCode();
        }
    
        string viewPath = customErrorsSection.DefaultRedirect;
        if (customErrorsSection.Errors != null)
        {
            CustomError customError = customErrorsSection.Errors[((int)httpStatusCode).ToString()];
            if (customError != null && string.IsNullOrEmpty(customError.Redirect))
            {
                viewPath = customError.Redirect;
            }
        }
    
        if (string.IsNullOrEmpty(viewPath))
        {
            return;
        }
    
        Response.Clear();
        Server.ClearError();
    
        var httpContextMock = new HttpContextWrapper(Context);
        httpContextMock.RewritePath(viewPath);
        RouteData routeData = RouteTable.Routes.GetRouteData(httpContextMock);
        if (routeData == null)
        {
            throw new InvalidOperationException(String.Format("Did not find custom view with the name '{0}'", viewPath));
        }
        string controllerName = routeData.Values["controller"] as string;
        if (String.IsNullOrEmpty(controllerName))
        {
            throw new InvalidOperationException(String.Format("No Controller was found for route '{0}'", viewPath));
        }
        routeData.Values["exception"] = exception;
    
        Response.TrySkipIisCustomErrors = true;
        RequestContext requestContext = new RequestContext(httpContextMock, routeData);
        IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
        IController errorsController = factory.CreateController(requestContext, controllerName);
        errorsController.Execute(requestContext);
    }