65.9K
CodeProject 正在变化。 阅读更多。
Home

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

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2010 年 9 月 17 日

CPOL

5分钟阅读

viewsIcon

67212

从基本页面控件到 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 的问题
© . All rights reserved.