在网站开发过程中,经常会遇到各种挑战,其中之一就是数据库服务器的故障。想象一下,开发了一个快速的网站,展示了编码技巧和技能,并且获得了客户的广泛赞誉。然而,在庆祝成功的夜晚,数据库服务器却突然失败了!所有的开发人员都在庆祝,没有人注意到日志文件越来越大,直到客户注意到并引发了危机。经过几个小时的紧张工作,数据库硬件恢复了,网站也恢复了往日的辉煌。但是,问题真的解决了吗?
作为一个优秀的网站开发人员,当然会启用ASP.NET中的自定义错误页面,甚至可能设置了服务器的部署属性以确保双重安全。在网站停机期间,每个访问网站的访客都会看到一个友好的消息,告知他们网站出现了问题,但很快就会恢复。也许甚至通过添加一些营销文案来减少潜在的影响,这些文案可以吸引访客在网站恢复后返回,这些文案正是搜索引擎所喜欢的。但是,如果网站在被索引时出现问题,那么错误消息可能会给带来麻烦。
公平地说,错误页面不太可能成为某个主题的权威排名。搜索引擎已经变得非常擅长发现这些错误,但这也不会帮助页面排名。当ASP.NET应用程序抛出一个未处理的异常时,它自然会返回一个500状态码(内部服务器错误)。如果没有启用自定义错误,那么就会得到这个状态码,这将导致“黄色死亡屏幕”。但是,如果启用了自定义错误处理,就会有一个巧妙的HttpModule检测到500状态码(内部服务器错误),并将这个状态码重定向到自定义错误页面。然后,这个页面成功渲染(毕竟,错误页面不会有错误!)并返回一个200状态码(OK)。
using System;
using System.Web;
using System.Net;
using System.Collections.Generic;
using System.Configuration;
using System.Web.Configuration;
namespace MartinOnDotNet.Website.Support
{
///
/// Handles errors in an SEO friendly manner
///
public class SeoErrorLoggingModule : IHttpModule
{
protected virtual void OnError(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
if (context != null && context.AllErrors != null)
{
foreach (Exception ex in context.AllErrors)
{
ex.Data["RawUrl"] = context.Request.RawUrl;
HttpException hex = ex as HttpException;
if (hex != null && hex.GetHttpCode() == (int)HttpStatusCode.NotFound)
{
Logging.Logger.LogWarning(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Requested File Not Found {0} ({1})", context.Request.RawUrl, context.Request.Url));
}
else
{
Logging.Logger.Log(ex);
}
}
}
HttpException httpException = context.Error as HttpException;
context.Response.Clear();
if (httpException != null)
{
context.Response.StatusCode = httpException.GetHttpCode();
}
else
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
if (context.IsCustomErrorEnabled && !context.Request.Browser.Crawler && !IsAnErrorPage(context.Request.RawUrl))
{
context.ClearError();
string path = GetPathForError(context, (HttpStatusCode)context.Response.StatusCode);
if (!string.IsNullOrEmpty(path))
{
context.Response.Redirect(path, true);
}
}
}
protected virtual string GetPathForError(HttpContext current, HttpStatusCode status)
{
CustomErrorsSection customErrors = WebConfigurationManager.GetSection("system.web/customErrors") as CustomErrorsSection;
foreach (CustomError ce in customErrors.Errors)
{
if (ce.StatusCode == (int)status)
return ce.Redirect;
}
return customErrors.DefaultRedirect;
}
protected virtual bool IsAnErrorPage(string path)
{
if (ErrorPages != null)
{
foreach (string s in ErrorPages)
{
if (path.IndexOf(s, StringComparison.OrdinalIgnoreCase) > -1)
return true;
}
}
return false;
}
protected virtual IEnumerable ErrorPages
{
get
{
CustomErrorsSection customErrors = WebConfigurationManager.GetSection("system.web/customErrors") as CustomErrorsSection;
foreach (CustomError ce in customErrors.Errors)
{
yield return ce.Redirect;
}
yield return customErrors.DefaultRedirect;
}
}
public void Dispose()
{
// clean-up code here.
}
public void Init(HttpApplication context)
{
// Below is an example of how you can handle LogRequest event and provide custom logging implementation for it
context.Error += new EventHandler(OnError);
}
}
}