asp.net中的URL重写功能

来源:互联网 发布:淘宝活动平台 编辑:程序博客网 时间:2024/05/01 07:23

1.3  URL重写功能

URL重写功能就是接受带有有效命名约定的URL,把它们转化为查询字符串。需要有效命名约定的两个原因是:将信息组织到逻辑层次结构中,以及隐藏查询字符串参数。本节将说明URL重写功能如何改进用户界面,描述实现URL重写功能的新旧方式,并给出一些代码来演示这个概念。

注意:

本节还添加了一些代码,来演示n层体系结构和数据绑定过程中的最佳实践方式,而不是使用较简单的数据源控件。


1.3.1  为什么要重写URL

看看博客是如何按时间组织的,就可以明白分层组织的含义。从用户的角度来看,下面的查询字符串是很难理解的:

http://www.someblogsite.com/username/?y=2005&m=01&d=31

上面的查询字符串返回2005年1月1日的博客项,这并不容易确定。我们可以修改代码,使其参数更有意义,如下:

http://www.someblogsite.com/username/?year=2005&month=01&day=31

无论查询字符串的参数如何表示,一般用户都很难理解它。最好用富有层次感的表示方式编写查询字符串,如下:

http://www.someblogsite.com/username/2005/January/31

对于站点的一般访问者,上面的URL不是很难理解。例如,如果用户删除了日期,就会得到1月的所有记录。

即使没有自然的层次结构或干脆没有参数,只要URL有一个有意义的名称,而不是使用查询参数,用户还是较容易理解它们的。

1.3.2  ASP.NET v1.1的窍门程序

在ASP.NET v1.x中执行URL重写功能的一个很好的资源是Scott Mitchell撰写的MSDN文章“URL Rewriting in ASP.NET”,它位于msdn.microsoft.com/library/default.asp?url= /library/en-us/dnaspp/html/urlrewriting.asp。在这篇文章中,Scott解释了如何通过HTTP模块和HTTP处理程序执行URL重写功能,并说明了它们的使用场合。他还建立了一个可重用的URL重写引擎,通过配置文件使用正则表达式。

1.3.3  ASP.NET v2.0 的替代品

在ASP.NET v2.0中,是通过urlMappings配置元素支持重写URL功能的。给web.config添加一个新项,来映射URL,如下所示:

<urlMappings enabled="true">

  <add url="~/Articles/AspDotNet/UrlRewriting"

mappedUrl="~/Articles.aspx?cat=1&id=16" />

</urlMappings>

用户看到的是url特性,而mappedUrl特性描述了实际请求的页面。在上面的urlMappings元素中,假定有一个文章页面根据类别和文章标识符动态返回文章。于是,url特性会显示首选的用户界面,但mappedUrl特性会显示实际的页面和请求的参数。

1.3.4  实现URL映射功能

以前笔者编写过一个示例应用程序,来说明如何使用这个功能。这是文章的一个变体,介绍了上述概念,但它是根据年份和月份来解释的。例如,文章应用程序允许选择年份,再选择月份。每个页面上都显示了可读的URL,我们可以利用该URL,简单地修改页面地址,来导航应用程序。程序清单1-6显示了文章应用程序的初始页面。

程序清单1-6  使用可读的URL标识年份的主页: Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs"

Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>.NET Article Archive</title>

</head>

<body>

    <form id="form1" runat="server">

        <h1>.NET Article Archive</h1>

        <p>

            Pick a Year:

        </p>

        <p>

            <asp:HyperLink ID="HyperLink1" runat="server"

                 NavigateUrl="~/2006">2006</asp:HyperLink><br />

            <asp:HyperLink ID="HyperLink2" runat="server"

                 NavigateUrl="~/2005">2005</asp:HyperLink>

        </p>

    </form>

</body>

</html>

HyperLink元素的NavigateUrl特性包含可读的URL,该URL要重写为查询字符串参数。同样,在用户选择一个年份后,就会看到如程序清单1-7所述的年份页面。

程序清单1-7  带有可读URL的年份页面: YearView.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="YearView.aspx.cs"

Inherits="YearView" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Articles for the Year</title>

</head>

<body>

    <form id="form1" runat="server">

        <h1>

            <asp:Label ID="lblTitle"

                       runat="server"

                       Text="Articles for the Year #">

            </asp:Label>

        </h1>

        <p>

            <asp:Panel ID="pnlMonths" runat="server" Height="50px"

Width="125px">

                <asp:HyperLink ID="hypJanuary"   runat="server"

                    NavigateUrl="~/YEAR/01">January</asp:HyperLink><br />

                <asp:HyperLink ID="hypFebruary"  runat="server"

                    NavigateUrl="~/YEAR/02">February</asp:HyperLink><br />

                <asp:HyperLink ID="hypMarch"     runat="server"

                    NavigateUrl="~/YEAR/03">March</asp:HyperLink><br />

                <asp:HyperLink ID="hypApril"     runat="server"

                    NavigateUrl="~/YEAR/04">April</asp:HyperLink><br />

                <asp:HyperLink ID="hypMay"       runat="server"

                    NavigateUrl="~/YEAR/05">May</asp:HyperLink><br />

                <asp:HyperLink ID="hypJune"      runat="server"

                    NavigateUrl="~/YEAR/06">June</asp:HyperLink><br />

                <asp:HyperLink ID="hypJuly"      runat="server"

                    NavigateUrl="~/YEAR/07">July</asp:HyperLink><br />

                <asp:HyperLink ID="hypAugust"    runat="server"

                    NavigateUrl="~/YEAR/08">August</asp:HyperLink><br />

                <asp:HyperLink ID="hypSeptember" runat="server"

                    NavigateUrl="~/YEAR/09">September</asp:HyperLink><br />

                <asp:HyperLink ID="hypOctober"   runat="server"

                    NavigateUrl="~/YEAR/10">October</asp:HyperLink><br />

                <asp:HyperLink ID="hypNovember"  runat="server"

                    NavigateUrl="~/YEAR/11">November</asp:HyperLink><br />

                <asp:HyperLink ID="hypDecember"  runat="server"

                    NavigateUrl="~/YEAR/12">December</asp:HyperLink>

            </asp:Panel>

        </p>

    </form>

</body>

</html>

程序清单1-7显示了HyperLink元素,其NavigateUrl特性设置为可读的URL。它为一年中的每个月包含一层,其中数字对应月份的顺序。为了正确处理这些URL,应编辑Web窗体YearView的代码文件,如程序清单1-8所示。

程序清单1-8  通过参数化的URL读取页面的查询字符串参数: YearView.aspx.cs

using System;

using System.Data;

using System.Configuration;

using System.Collections;

using System.Web;

using System.Web.Security;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using System.Web.UI.HtmlControls;

public partial class YearView : System.Web.UI.Page

{

    string year;  // passed in query string

    protected void Page_Load(object sender, EventArgs e)

    {

        // we'll use this in multiple methods

        year = Request.QueryString["year"];

        // set page title

        SetTitle();

        // configure months to refer to proper page

        SetMonths();

    }

    /// <summary>

    /// configure months to refer to proper page

    /// </summary>

    private void SetMonths()

    {

        foreach (Control ctrl in pnlMonths.Controls)

        {

            HyperLink monthLink = ctrl as HyperLink;

            if (monthLink != null)

            {

                monthLink.NavigateUrl =

                    monthLink.NavigateUrl.Replace("YEAR", year);

            }

        }

    }

    /// <summary>

    /// set page title

    /// </summary>

    private void SetTitle()

    {

        lblTitle.Text = "Articles for the Year " + year;

    }

}

程序清单1-8中的Page_Load方法从查询字符串中提取year参数,使之可用于后续的方法。SetTitle方法使用这个值重写有正确年份的页面标题。

另外还要注意程序清单1-8中HyperLink控件的NavigateUrl属性,它们在URL的年份位置上都包含“YEAR”文本。这会使代码更富有动感,因为根据年份,这些NavigateUrl属性必须重写。这就是程序清单1-8中SetMonths方法的作用。程序清单1-8中的代码故意把每个HyperLink控件放在一个面板中,以便在代码中使用其Controls集合。因此,在代码文件中,SetMonths方法可以迭代Controls集合,用一个简单的string.Replace方法调用来设置年份。为了使这个内置的URL重写功能可用于这个应用程序,web.config文件中有一个urlMapping元素,如程序清单1-9所示。

程序清单1-9  web.config文件中的urlMapping元素可以在ASP.NET v2.0中重写URL

<?xml version="1.0"?>

<configuration>

  <system.web>

    <urlMappings>

      <add url="~/2006"

           mappedUrl="~/YearView.aspx?year=2006"/>

      <add url="~/2006/01"

           mappedUrl="~/MonthView.aspx?year=2006&amp;month=01"/>

      <add url="~/2006/02"

           mappedUrl="~/MonthView.aspx?year=2006&amp;month=02"/>

      <add url="~/2005"

           mappedUrl="~/YearView.aspx?year=2005"/>

      <add url="~/2005/01"

           mappedUrl="~/MonthView.aspx?year=2005&amp;month=01"/>

      <add url="~/2005/02"

           mappedUrl="~/MonthView.aspx?year=2005&amp;month=02"/>

    </urlMappings>

    <compilation debug="true"/>

  </system.web>

</configuration>

在程序清单1-9的每个add元素中,可重写的URL转化为mappedUrl,mappedUrl是实际的地址,也是发送给页面的查询字符串。发音符号(~)表示每个页面相关的应用程序目录。必须使用&amp;替代&符号,将参数分隔开。这里省略了月份,以缩短程序清单。

要查看文章列表,用户选择了他感兴趣的月份。程序清单1-10演示了MonthView.aspx的执行过程。在程序清单1-10中使用了GridView控件,是因为这里需要为多个数据行格式化输出,并把它绑定到ObjectDataSource控件上。GridView控件是ASP.NET v2.0中的新增控件,它替代了DataGrid控件。

程序清单1-10  使用GridView读取查询参数:MonthView.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="MonthView.aspx.cs"

Inherits="MonthView" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Articles for the Month</title>

</head>

<body>

    <form id="form1" runat="server">

        <h1>

            Requested Articles:</h1>

        <br />

        <asp:GridView ID="GridView1" runat="server"

            AutoGenerateColumns="False" DataSourceID="ArticlesODS">

            <Columns>

                <asp:BoundField DataField="Year"

                    HeaderText="Year" SortExpression="Year" />

                <asp:BoundField DataField="Month"

                    HeaderText="Month" SortExpression="Month" />

                <asp:BoundField DataField="Title"

                    HeaderText="Title" SortExpression="Title" />

                <asp:BoundField DataField="Content"

                    HeaderText="Content" SortExpression="Content" />

            </Columns>

        </asp:GridView>

        <asp:ObjectDataSource ID="ArticlesODS" runat="server"

            SelectMethod="GetArticles" TypeName="Articles">

            <SelectParameters>

                <asp:QueryStringParameter DefaultValue="2006"

                    Name="year" QueryStringField="year"

                    Type="String" />

                <asp:QueryStringParameter DefaultValue="01"

                    Name="month" QueryStringField="month"

                    Type="String" />

            </SelectParameters>

        </asp:ObjectDataSource>

    </form>

</body>

</html>

为了提倡设计良好的n层体系结构,这里使用业务对象和正式的数据访问层来实现文章的管理。因此在MonthView.aspx页面中使用了ObjectDataSource控件。很容易把查询字符串参数直接映射到Articles类的GetArticles方法上,如程序清单1-11所示。

程序清单1-11  Articles类包含一组article对象: articles.cs

using System;

using System.Collections.Generic;

/// <summary>

/// List of Articles

/// </summary>

public class Articles : List<Article>

{

    public List<Article> GetArticles(string year, string month)

    {

        ArticleData dal = new ArticleData();

        dal.GetArticles(this, year, month);

        return this;

    }

}

程序清单1-11中的Articles类利用C#编程语言中新增的泛型特性,创建了一个强类型化的Article对象集合。通过继承List<Article>,可以获得泛型的优势,给类型指定更容易理解的名称。程序清单1-12列出了用ArticleData类表示的数据访问层。

程序清单1-12  ArticleData类用数据源中的新文章填充当前的列表:ArticleData.cs

using System;

using System.Collections;

using System.Collections.Generic;

using System.Data;

using System.Web;

/// <summary>

/// Summary description for ArticleData

/// </summary>

public class ArticleData

{

    public void GetArticles(List<Article> articles, string year, string

month)

    {

        DataSet dsArticles = new DataSet();  

dsArticles.ReadXml(HttpContext.Current.Server.MapPath("Articles

.xml"));

        DataView dvArticles = new DataView(dsArticles.Tables["article"]);

        dvArticles.RowFilter =

            "year = '" + year + "' " +

            "and month = '" + month + "'";

        Article currArticle = null;

        IEnumerator articleRows = dvArticles.GetEnumerator();

        while (articleRows.MoveNext())

        {

            DataRowView articleRow = (DataRowView)articleRows.Current;

            currArticle = new Article(

                (string)articleRow["year"],

                (string)articleRow["month"],

                (string)articleRow["title"],

                (string)articleRow["content"]);

            articles.Add(currArticle);

        }

    }

}

这个类把一个XML文件(参见程序清单1-13)加载到DataSet中。它根据所传送的年份和月份参数,使用DataView过滤结果。Article类是一个业务对象,所以最终在UI层中绑定到GridView上。当然,UI层不需要知道数据是来自于XML文件或通过ADO.NET组件进行了处理。它很容易在以后进行修改,以满足新的要求。另外,GridView可以在Generic集合中显示业务对象,因此,我们通过articles参数把数据作为List<Article>传送回。

程序清单1-13  文章数据通过XML文件来表示: Articles.xml

<?xml version="1.0" encoding="utf-8" ?>

<articles>

  <article>

    <year>2005</year>

    <month>01</month>

    <title>Title1</title>

    <content>This is the text of Title1.</content>

  </article>

  <article>

    <year>2005</year>

    <month>02</month>

    <title>Title2</title>

    <content>This is the text of Title2.</content>

  </article>

  <article>

    <year>2005</year>

    <month>02</month>

    <title>Title3</title>

    <content>This is the text of Title3.</content>

  </article>

  <article>

    <year>2006</year>

    <month>01</month>

    <title>Title4</title>

    <content>This is the text of Title4.</content>

  </article>

  <article>

    <year>2006</year>

    <month>01</month>

    <title>Title5</title>

    <content>This is the text of Title5.</content>

  </article>

  <article>

    <year>2006</year>

    <month>02</month>

    <title>Title6</title>

    <content>This is the text of Title6.</content>

  </article>

</articles>

程序清单1-12中的代码把一个XML文件(参见程序清单1-13)用作数据源,完成了一个非常简单的实践。ArticlesData类使用该数据填充Article对象,如程序清单1-14所示。

程序清单1-14  Article类是一个业务对象,它要绑定到UI层的GridView上: Article.cs

using System;

using System.Data;

using System.Configuration;

using System.Web;

using System.Web.Security;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using System.Web.UI.HtmlControls;

/// <summary>

/// Represents an Article

/// </summary>

public class Article

{

    private string m_year;

    public string Year

    {

        get { return m_year; }

        set { m_year = value; }

    }

    private string m_month;

    public string Month

    {

        get { return m_month; }

        set { m_month = value; }

    }

 

    private string m_title;

    public string Title

    {

        get { return m_title; }

        set { m_title = value; }

    }

    private string m_content;

    public string Content

    {

        get { return m_content; }

        set { m_content = value; }

    }

 public Article(string year, string month, string title, string content)

 {

        Year = year;

        Month = month;

        Title = title;

        Content = content;

 }

}

Articles类中的数据通过属性表示为其公共接口,具有封装特性。这将允许在需要时改变底层实现方式,包括添加业务规则和验证逻辑。

注意:

如果使用数据源控件来替代上述代码中的ObjectDataSource控件,就不能添加业务规则了。不使用数据源控件是为了简化这个例子中的代码,它还有另一个好处。正确使用Object DataSource控件,可以设计应用程序,使其具备灵活性和可维护性。

使用ASP.NET v2.0的URL映射特性,似乎有很大的优势,但它有一个缺陷:不能使用正则表达式。本节中的文章示例使用正则表达式,将得到更好的效果。注意,urlMappings元素包含的代码非常类似,只能通过年份或月份来区分。通过一个正则表达式,用参数映射年份或月份,可以更好地实现这个功能。如果使用Scott Mitchell的URL重写引擎,配置将如下所示:

<RewriterConfig>

   <Rules>

      <!-- Rules for Blog Content Displayer -->

      <RewriterRule>

         <LookFor>~/(/d{4})/(/d{2})/Default/.aspx</LookFor>

         <SendTo><![CDATA[~/YearView.aspx?year=$1&month=$2]]></SendTo>

      </RewriterRule>

      <RewriterRule>

         <LookFor>~/(/d{4})/Default/.aspx</LookFor>

         <SendTo>~/YearView.aspx?year=$1</SendTo>

      </RewriterRule>

   </Rules>

</RewriterConfig>

这是一个简单的一次性的配置。但在Visual Studio 2005实现方式(参见程序清单1-9中配置文件的urlMapping元素)中,必须为每个年份和每个月份更新该配置文件。这是不切实际的,因为这个文件会变大,且需要手动干预。对于有正则表达式的URL重写功能,建议使用Scott Mitchell文章中提及的窍门程序。ASP.NET v2.0的URL映射特性只能在最简单的情况下使用。

1.4  小结

本章介绍了ASP.NET v1.1的先驱发明的几个窍门程序。在Microsoft把这些窍门程序转变为ASP.NET v2.0的主要产品特性时,这些人有很大的影响。Wizard窍门程序变成Wizard控件。模板、继承和用户控件Master Page窍门程序变成ASP.NET v2.0中的Master Page。URL重写窍门程序影响了ASP.NET v2.0中的URL映射,在需要正则表达式时,URL重写窍门程序一直以来都是实现URL重写功能的首选方法。

 
原创粉丝点击