在 ASP.NET 3.5 中处理 301 重定向





0/5 (0投票)
从基本页面控件到 HTTP 模块的 ASP.NET 中的 301 重定向处理
引言
您正在构建一个新网站,有很多新页面和很多旧页面将不再存在,告诉外部链接源、搜索引擎、爬虫等页面已移动可能是一个好主意。通过“301 Moved Permanently”来通知传入请求页面的移动或替换。
以下是一些适合使用重定向的情况
- 网站彻底重新设计
- 单个页面移动
- 从静态网站 htm 迁移到 aspx
- 您不想失去搜索引擎排名
- 您无法定义所有外部链接
背景:什么是“301 Moved Permanently”
w3.org 声明:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
10.3.2 301 Moved Permanently (永久移动)
请求的资源已被分配新的永久 URI,将来对该资源的任何引用都应使用返回的 URI 之一。具有链接编辑功能的客户端应该自动将对 Request-URI 的引用重新链接到服务器返回的一个或多个新引用,如果可能的话。此响应是可缓存的,除非另有说明。
新的永久 URI 应由响应中的 Location 字段给出。除非请求方法是 HEAD,否则响应实体应包含一个带有指向新 URI 的超链接的简短超文本注释。
如果在响应 GET 或 HEAD 以外的请求时收到 301 状态码,则用户代理不得自动重定向请求,除非用户可以确认,因为这可能会更改发出请求的条件。
基本重定向
如果旧页面仍然存在,则可以在 Page_Load
事件中添加如下代码:
protected void Page_Load(object sender, EventArgs e)
{
// Flag Page has moved
Response.Status = "301 Moved Permanently";
// Ask Browser to move to new page
Response.AddHeader("Location", "NewPage_Redirect1.aspx");
}
上面的代码有优点也有缺点
优点
- 快速
- 简单
缺点
- 您需要从现在开始在应用程序中维护该页面
- 不适用于可下载内容,如图片、PDF、媒体等。
- 您仍然需要在新网站中保留旧的文件夹结构。
- 不适用于目录/文件夹重定向。
- 从 Java、Apache 迁移到 IIS 时不起作用
从基本重定向扩展
因此,我们需要一种方法来提供一个干净的网站,并能够处理混合的传入请求并将它们指向正确的页面或文件夹。另外,处理非 Web 应用程序文件,如 pdf 和 Word 文档,也会很好。
实现这一点的最佳方法是使用自定义 HTTP 模块,该模块将处理请求并将客户端浏览器指向正确的页面。
创建自定义 HTTP 模块进行重定向
首先,您需要创建一个继承自 IHttpModule
接口的类,并实现两个成员,如下所示。
注意:稍后我们将填写这些成员
public class HttpRedirect : IHttpModule
{
public void Init(HttpApplication context)
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
}
为重定向类在 Web.Config 中创建条目
下面是名为“HttpRedirect
”的 Redirect
类的条目,它将在传入请求时触发。
<modules>
<add name="Redirects" type="Redirects301.HttpRedirect,Redirects301"/>
<add name="ScriptModule" preCondition="managedHandler"
type="System.Web.Handlers.ScriptModule, System.Web.Extensions,
Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</modules>
重定向列表
我大约有 100 个重定向,其中一些可能会更改,另一些可能会稍后添加,所以我将它们放在一个 XML 文件中,这样我就可以加载并修改它们,而无需重新构建应用程序。
因此,有一个名为 **Redirect301.xml** 的 XML 文件,其中包含重定向。
<?xml version="1.0" encoding="utf-8" ?>
<Redirects>
<Redirect>
<Description>Change of Index</Description>
<OldPage>/index.htm</OldPage>
<NewPage>/index.aspx</NewPage>
<File>FALSE</File>
</Redirect>
<Redirect>
<Description>Directory </Description>
<OldPage>/Profile</OldPage>
<NewPage>/users</NewPage>
<File>FALSE</File>
</Redirect>
<Redirect>
<Description>PDF Redirect</Description>
<OldPage>/lib/docs/TermsOfBusiness.pdf</OldPage>
<NewPage>/docs/TOB.pdf</NewPage>
<File>TRUE</File>
</Redirect>
</Redirects>
其内容具有以下功能
- Description(描述):代码中无作用,纯粹用于描述重定向
- OldPage(旧页面):这是旧页面,也是您将要查找的传入请求
- NewPage(新页面):OldPage 请求的目标位置。
- File(文件):NewPage 是文件下载而不是 IIS 中的支持格式;我将其用于 PDF。
将此位置添加到 Web.Config
如下所示的简单应用程序键,HttpRedirect
类使用它来查找重定向 XML。
<appSettings>
<add key="RedirectXMLDoc" value="/Redirects301.xml"/>
</appSettings>
重定向类
用于以可用的格式存储重定向。这大致对应于 Redirect301.xml 文件中的内容。
namespace Redirects301
{
public class Redirect
{
public string OldPage { get; set; }
public string NewPage { get; set; }
public bool TransferFileType { get; set; }
}
}
核心业务:HttpRedirect 类
这是 HttpRedirect
类的完整列表。
using System;
using System.Collections.Generic;
using System.Web;
using System.Linq;
using System.Web.Configuration;
using System.Xml.Linq;
namespace letsure
{
public class HttpRedirect : IHttpModule
{
public void Init(HttpApplication ap)
{
ap.BeginRequest += new EventHandler(ap_BeginRequest);
}
/// <summary>
/// Filename of the Redirect301 xml document
/// </summary>
static string Xmldoc
{
get
{
string filename = WebConfigurationManager.AppSettings["RedirectXMLDoc"];
return HttpContext.Current.Server.MapPath(filename);
}
}
static void ap_BeginRequest(object sender, EventArgs e)
{
// Get current HttpApplication Request
HttpApplication hxa = (HttpApplication) sender;
//Get Incoming Request for page.
string incomingText = hxa.Context.Request.Url.AbsolutePath.Trim();
// This deals with ISS issues regarding it does not
// service non-existant pages
// so create a custom 404.aspx page change ISS to attach
// custom 404 to this page
// and this code will handle this.
if (hxa.Request.Url.LocalPath.Contains("404.aspx"))
{
incomingText = hxa.Request.Url.Query.ToLower();
//script away the errorpath from query string to get the original
//requested url
incomingText = incomingText.Replace("?aspxerrorpath=", string.Empty);
// Fix for SERVER IIS6
incomingText = incomingText.Replace("?404;", string.Empty);
string schemeHost = hxa.Request.Url.Scheme + "://"
+ hxa.Request.Url.Host;
incomingText = incomingText.Replace(schemeHost, string.Empty);
// Fix for Ports numbers on ports that are not default
string portvalue = ":" + hxa.Request.Url.Port;
incomingText = incomingText.Replace( portvalue, "");
}
// Try and Check for redirect
try
{
// Check if in list
var results = from r in Redirects()
where incomingText.ToLower() == r.OldPage.ToLower()
select r;
//Redirect if required.
if (results.Count() > 0)
{
//Select First Redirect if we have multiple matches
Redirect r = results.First();
// Add Moved Permanently 301 to response
hxa.Response.Status = "301 Moved Permanently";
//Test the Response is an attachment type ie not hmtl,
//aspx, .htm, html, etc....
if (r.TransferFileType)
{
hxa.Response.AddHeader("content-disposition",
"attachment; filename=filename");
}
// Go to new page
hxa.Response.AddHeader("Location", r.NewPage);
//hxa.Response.End();
}
}
catch (Exception)
{
//Redirect to page not found if we have an error
//hxa.Response.AddHeader("Location", @"\404.aspx");
throw;
}
}
/// <summary>
/// Dispose; there is nothing to dispose.
/// </summary>
public void Dispose() { }
/// <summary>
/// Return a collection of Redirect
/// </summary>
/// <returns></returns>
private static IEnumerable<Redirect> Redirects()
{
//Get Collection of Redirects
var results = new List<Redirect>();
//Get Redirect File XML name for
XDocument xdocument = GetXmLdocument();
var redirects = from redirect in xdocument.Descendants("Redirect")
select new
{
oldpage = redirect.Element("OldPage").Value,
newpage = redirect.Element("NewPage").Value,
file = redirect.Element("File").Value
};
foreach (var r in redirects)
{
var redirect = new Redirect
{
NewPage = r.newpage,
OldPage = r.oldpage,
TransferFileType = Convert.ToBoolean(r.file)
};
results.Add(redirect);
}
return results;
}
/// <summary>
/// Get Redirect Doc from Cache
/// </summary>
/// <returns></returns>
private static XDocument GetXmLdocument()
{
//create empty return doc
XDocument xmldocument;
//check if Cache is null; if so add it
if (HttpRuntime.Cache["RedirectXMLDoc"] == null)
{
xmldocument = XDocument.Load(Xmldoc);
HttpRuntime.Cache["RedirectXMLDoc"] = xmldocument;
}
else
{
//Get from Cache.
xmldocument = (XDocument) HttpRuntime.Cache["RedirectXMLDoc"];
}
return xmldocument;
}
}
}
<appSettings> </appSettings>
关注点
重定向到代码提供的文件,因为它添加了一个头部来告诉客户端浏览器期望一个文件。我可以测试传入请求的扩展名并决定它是文件还是其他内容,但在 HTML 中添加一些内容更简单,并且可以提供更多的控制。
//Test the Response is an attachment type ie not hmtl, aspx, .htm, html, etc....
if (r.TransferFileType)
{
hxa.Response.AddHeader("content-disposition", "attachment; filename=filename");
}
问题
有些人可能已经注意到一个潜在的性能问题?
问题在于,对于每个请求,都必须打开、读取和搜索 Redirect
XML 文档。这绝对不是理想的,应该将其缓存到应用程序级别,并且应广大用户要求,我已经添加了此功能。
文件夹重定向
此代码不重定向请求的文件夹;也就是说,如果您有一个指向 /old/site.aspx 的链接,并且您想将其重定向到 /new/site.aspx。设置一个 oldfile=/old newfile=/new 的规则不会正确重定向到页面。但是,如果您的传入请求是 /old,它将作为文件夹级别的重定向到 /new,因为它将为该文件夹拾取您的默认页面。
测试
测试,再次测试,然后让别人再测试一遍。不要假设我的代码或任何其他人的代码能按预期工作,这对我来说是有效的,但您可能会遇到问题。
用途
我在以下环境中使用了此代码,没有任何问题:
- VS 2008
- .NET FrameWork 3.5
- IIS 6-7
下载次数
我认为您不需要任何东西,除非您无法复制或粘贴。
历史
- 版本 1.000.001 2010年9月17日
- 首次发布
- 版本 1.000.002 2011年3月4日
- 已添加缓存
- 修复了 IIS 5/6/7 的问题