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&month=01"/>
<add url="~/2006/02"
mappedUrl="~/MonthView.aspx?year=2006&month=02"/>
<add url="~/2005"
mappedUrl="~/YearView.aspx?year=2005"/>
<add url="~/2005/01"
mappedUrl="~/MonthView.aspx?year=2005&month=01"/>
<add url="~/2005/02"
mappedUrl="~/MonthView.aspx?year=2005&month=02"/>
</urlMappings>
<compilation debug="true"/>
</system.web>
</configuration>
在程序清单1-9的每个add元素中,可重写的URL转化为mappedUrl,mappedUrl是实际的地址,也是发送给页面的查询字符串。发音符号(~)表示每个页面相关的应用程序目录。必须使用&替代&符号,将参数分隔开。这里省略了月份,以缩短程序清单。
要查看文章列表,用户选择了他感兴趣的月份。程序清单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重写功能的首选方法。
本文转自周金桥51CTO博客,原文链接: http://blog.51cto.com/zhoufoxcn/166978
,如需转载请自行联系原作者