Scott Guthrie 博客中文版

Scott Guthrie's Blog on ASP.NET and .NET (英文原版地址:http://weblogs.asp.net/scottgu)

   博客堂 :: 首页 :: 联系 :: RSS 2.0 ::
随笔 - 249, 评论 - 1156, 引用 - 436

导航

标签

每月存档

最新留言

广告

Orcas中C#语言的新特性:自动属性,对象初始化器,和集合初始化器

【原文地址】New C# "Orcas" Language Features: Automatic Properties, Object Initializers, and Collection Initializers
【原文发表日期】 Thursday, March 08, 2007 11:01 PM

上个星期,我们发布了我们的Visual Studio和.NET框架Orcas版三月份的CTP,这是个谁都可以下的免费下载,同时提供VPC镜像(允许你在一个虚拟机里运行它)以及单独的安装文件(注:如果你在运行Vista的话,你要确认你只使用VPC版本)。你可以在这里下载。

几个星期前,我曾在博客上讨论过Orcas中针对ASP.NET开发人员的一些重大的改进。如果你还没有读过这个贴子的话,我强烈建议你在这里读一下。我认为你会非常喜欢该帖子讨论的新特性的。

除了那些框架和工具类的精彩新特性外,我认为开发人员(包括所有的.NET应用类型的开发人员)会非常喜爱Orcas的一件事情是,VB和C#将包含一些新语言特性和改进。这些语言改动将以既微妙又深刻的方式改进我们的开发体验,势将改进效率,减小我们需要键入的代码量。

在下几个星期里,我将试着在博客里讨论几个这些语言的改进之处,示范如何把它们使用在一起,来产生一些非常强有力的结果。

新的C#语言特性:自动属性(Automatic Properties)

如果你现在是C#开发人员的话,你大概非常习惯编写象下面这个代码片段一样带有基本属性的类型:

    public class Person {

        
private string _firstName;
        private string 
_lastName;
        private int 
_age;
        
        public string 
FirstName {

            
get {
                
return _firstName;
            
}
            
set {
                _firstName 
= value;
            
}
        }

        
public string LastName {

            
get {
                
return _lastName;
            
}
            
set {
                _lastName 
= value;
            
}
        }        
        
        
public int Age {

            
get {
                
return _age;
            
}
            
set {
                _age 
= value;
            
}
        }
    }

注意,我们在属性的geter/setter中实际上并没有添加什么逻辑,我们只是将get/set实施到了一个成员变量。我们不禁要问这样一个问题:为什么不直接使用成员变量而使用属性呢?这是因为,向外面呈现公开的成员变量有很多不好的地方。二个最大的问题是:1) 你无法轻易地对成员变量做数据绑定,2) 如果你从类中向外呈现成员变量的话,之后,你不重新编译那些引用老的类的任何程序集,就无法将它们改成属性(譬如,要添加验证逻辑到setter里)。

Orcas中发布的新C#编译器通过一个叫“自动属性(automatic properties)”的语言特性提供了一个优雅的方式来使得你的编码更加简洁,同时还保持属性的灵活性。自动属性允许你避免手工声明一个私有成员变量以及编写get/set逻辑,取而代之的是,编译器会自动为你生成一个私有变量和默认的get/set 操作。

譬如,使用自动属性,我现在可以将上面的代码改写成:

    public class Person {
    
        
public string FirstName {
            
get; set;
        
}

        
public string LastName {
            
get; set;
        
}        
        
        
public int Age {
            
get; set;
        
}
    }

或者,我想更简明的话,我可以将空白的地方做进一步压缩,象这样:

    public class Person {
        
public string FirstName { get; set; }
        
public string LastName  { get; set; }        
        
public int    Age       { get; set; }
    }

当Orcas版中的C#编译器遇上象上面这样的空的get/set属性的话,它会自动为你在类中生成一个私有成员变量,对这个变量实现一个公开的getter 和setter。这么做的好处是,从类-合同(type-contract)的角度来看,这个类跟我们上面第一个有点冗长的实现看上去完全一样,这意味着,不象公开的成员变量,在将来,我可以在我的属性setter实现中添加验证逻辑,而不用对引用我的类的任何外部组件做改动。

Bart De Smet对使用Orcas三月份CTP版中的自动属性时内部发生的情形作了精彩的描述,你可以在这里阅读他的精彩相关帖子

C#和VB语言的新特性:对象初始化器(Object Initializers)

.NET框架中的类型非常依赖于属性的使用。当生成对象实例和使用新的类型时,写出象下面这样的编码是非常常见的情形:

   Person person = new Person();
   
person.FirstName "Scott";
   
person.LastName "Guthrie";
   
person.Age 32;

你有没有想要把这样的编码简化过(也许将其安排在一行上)?使用Orcas中的C#和VB语言编译器的话,你现在可以利用一个称为“对象初始化器(object Initializers)”的“语法甜头(syntactic sugar)”语言特性来做些简化,将上述代码重写为:

  Person person = new Person { FirstName="Scott", LastName="Guthrie", Age=32 };

然后,编译器就会自动地生成合适的属性setter代码,保持跟前面较冗长的代码例子同样的语意。

除了在初始化类时设置简单的属性值外,对象初始化器特性也允许我们设置更复杂的嵌套(nested)属性类型。譬如,假如我们在上面定义的每个Person类型也拥有一个属于Address类型的叫“Address”的属性。我们可以编写下面这样的代码来生成一个新的Person对象,同时设置它的属性,象这样:

   Person person = new Person {
      FirstName 
"Scott",
      LastName 
"Guthrie"
      
Age 32,
      Address 
= new Address {
         Street 
"One Microsoft Way",
         City 
"Redmond",
         State 
"WA",
         Zip 
98052
      
}
   }
;

Bart De Smet对使用Orcas三月份CTP版中的对象初始化器时内部发生的情形也作了精彩的描述,你可以在这里阅读他的精彩相关帖子

C#和VB语言的新特性:集合初始化器(Collection Initializers)

对象初始化器很棒,它极大地简化了把对象添加到集合的做法。譬如,假如我要把三个人加到一个基于泛型的类型为Person的List集合中去的话,我可以写下面这样的编码:

  List<Person> people = new List<Person>();
            
  
people.Add( new Person { FirstName "Scott", LastName "Guthrie", Age 32 } );
  
people.Add( new Person { FirstName "Bill", LastName "Gates", Age 50 } );
  
people.Add( new Person { FirstName "Susanne", LastName "Guthrie", Age 32 } );
 

对这个例子,跟我使用C# 2.0编译器要输入的代码相比,单独使用新的对象初始化器特性就能省去12行额外的代码。

但Orcas版中的C#和VB编译器允许我们更进一步,现在同时支持“集合初始化器(collection initializers)”,这允许我们避免要写多个Add语句,省下更多的键盘操作:

   List<Person> people = new List<Person> {
      
new Person { FirstName "Scott", LastName "Guthrie", Age 32 },
      
new Person { FirstName "Bill", LastName "Gates", Age 50 },
      
new Person { FirstName "Susanne", LastName "Guthrie", Age 32 }
   }
;

当编译器遇上上面这样的句法时,它会自动为我们生成象前面的例子一样的集合插入编码。

结语

作为开发人员,我们现在拥有了简明得多的方式来定义对象,对它们初始化,将它们加入集合内。在运行时,其语意,跟今天的较长的版本的语意,是完全一样的(所以你不必担心行为会改变)。但现在你不需输入那么多字符了,你的代码将既干净又简明。

在不远的将来,我将发表更多的博客贴子来讨论Orcas版中更多的语言上的改进,包括扩展方法(Extension Methods),Lambdas和匿名类。然后,我将对LINQ做深入讨论,示范它是如何利用所有这些特性来提供一个非常优雅的方式来查询和与数据交互的。

希望本文对你有所帮助,

Scott

标签: , ,

posted on 2007-03-11 09:52:00 by scottgu  评论(57) 阅读(27823)

Scott Guthrie射击甜圈列兵的录像(以及一些好的ASP.NET AJAX链接)

【原文地址】Video of Scott Guthrie Shooting Private Donut (and some good ASP.NET AJAX links)
【原文发表日期】 Monday, March 05, 2007 11:09 PM

偶尔地,作为我工作的一部分,我有机会做些酷酷的非传统的事情。2个星期前,我在由Rooster Teeth制作的“红方(Red)”对“蓝方(Blue)”的Halo对抗战录像中出演一个角色,你可以在这里在线观看这个录像(3分钟长)。

下面是我在片子中的一个屏幕截图,就在我第一次射击“甜圈列兵(Private Donut)”之后(你要看了录像才能理解我的意思)。

 

我也在录像里花了一些时间给“红方”队员解释ASP.NET AJAX 能提供的功能。

ASP.NET AJAX 控件工具包更新版发布了

其他ASP.NET AJAX 新闻: ASP.NET AJAX 控件工具包开发队伍上个星期推出了ASP.NET AJAX 控件工具包的一个更新版本(是免费的,共享源码的库,包含了由微软和非微软开发人员共同开发的34个ASP.NET AJAX控件)。这个更新版包含了一堆很棒的新特性:

你可以在这里在线把玩和下载 ASP.NET AJAX 控件工具包。

免费微软学习课程:使用 Microsoft ASP.NET AJAX 扩展来开发增强的Web体验

微软学习中心发布了一个免费的在线诊所课程(free premium online clinic),该课程提供了一个很好的方式来学习如何使用ASP.NET AJAX。该课程的内容可以在线免费观看,它讨论了AJAX 控件和如何全面使用ASP.NET AJAX,它有2个小时之长。你可以在这里在线观看该课程

更多的 ASP.NET AJAX 《我怎么做?》 录像

Stagner 一直忙着制作更多的ASP.NET AJAX 《我怎么做?》 录像 (我上个月曾在这个博客贴子里写过关于这个系列的事情)。他上个星期刚发布了3个新录像:

想看Joe的更多的ASP.NET AJAX 录像的话,请看一下www.asp.net上的ASP.NET AJAX录像部分

Damian Mehers(他实现了上面提到的 ASP.NET AJAX 控件工具包里的ListSearch 控件)也发布了一个关于 ASP.NET AJAX和ASP.NET AJAX 控件工具包的很酷的录像,你可以在这里观看

ASP.NET AJAX的下载和部署情况

ASP.NET AJAX 1.0在正式发布之后的6个星期内,就被下载了几十万次之多。上个星期,我们从最近一个对Internet所做的web爬虫研究得到的结果中发现,超过6万个独特Internet网站在第一个月里就已经部署了使用ASP.NET AJAX V1.0 最终版的网站。点击这里看一下其中一些网站的屏幕截图,阅读一下对建造这些网站的开发人员的采访。

希望本文对你有所帮助,

Scott

posted on 2007-03-07 04:56:00 by scottgu  评论(11) 阅读(10046)

技巧/诀窍:把ASP.NET安全与经典ASP和非ASP.NET URLs集成

【原文地址】Tip/Trick: Integrating ASP.NET Security with Classic ASP and Non-ASP.NET URLs
【原文发表日期】 Sunday, March 04, 2007 6:17 PM

别人经常问我的一个问题是,“我怎么能把ASP.NET安全与经典ASP和其他非ASP.NET URLs集成起来?” 具体地来说,他们想知道他们是否能把ASP.NET中的表单认证,基于角色的安全, 以及URL授权等特性与经典ASP,PHP,JSP,.HTM, .JPG以及其他的非ASP.NET URLs集成起来。

好消息是,在今天的ASP.NET 2.0和IIS 6.0中,这非常容易,到IIS 7.0时段时,这将会更加容易。下面的博客帖子将示范如何把ASP.NET的表单认证和登录/成员特性与经典ASP和静态的.HTML文件集成起来。

想了解如何实现这个功能(以及如何把象基于ASP.NET角色的授权与经典ASP应用集成)的更详细的按部就班的示范的话,请阅读Stefan Schackow的精彩好书《ASP.NET 2.0 安全, 成员和角色管理(ASP.NET 2.0 Security, Membership, and Role Management)》中的第六章。

IIS 6.0的通配符映射一些背景知识

Windows Server 2003中的IIS 6.0中添加了对一个被称之为“通配符映射(wildcard mappings)”的ISAPI特性的支持。通配符映射提供了一个方式来配置IIS,使得进入服务器的所有请求首先被转到一个或多个ISAPI扩展作处理。IIS 6.0中的通配符映射一个非常酷的事情是,在处理通配符扩展的ISAPI扩展结束之后,它能使得IIS把请求的控制传递到IIS中通常处理该请求的扩展或内部处理器那里去。

ASP.NET 2.0包括了对利用这个通配符映射特性的内置支持。这允许你在现有的处理非ASP.NET URL(譬如,.asp, .php 或 .htm请求)的ISAPI扩展执行之前和之后运行ASP.NET编码或者你自己的自定义编码。

我们可以使用这个特性来启用一堆酷的集成特性,包括ASP.NET认证和授权特性,来为web服务器上的所有URLs设置安全。

如何配置IIS 6.0通配符映射

为这个例子起见,我将在IIS 6.0管理工具里创建一个新的IIS应用,叫做“wildcardtest”。它指向一个将含有default.aspx, login.aspx, test.asp 和 test.htm几个文件的目录(这后面2个文件代表了通常不由ASP.NET处理的资源):

在默认情形下,当一个对.aspx网页的URL请求到达该应用时,ASP.NET ISAPI 将处理这个请求。在默认情形下,当一个对test.asp的URL请求到达该应用时,经典的ASP ISAPI将处理这个请求,没有ASP.NET编码会运行的。当一个对test.htm的URL请求到达该应用时,IIS6会在内部处理这个请求,同样地,没有ASP.NET编码会运行的。

我们将通过对该应用启用通配符映射来改变这种情形,配置ASP.NET在到达该服务器的所有请求之前和之后运行些编码。想这么做的话,在IIS管理工具里右击当前应用,选择其上的“属性”上下文菜单选项。这会打开应用的属性对话框:

然后,你可以点击“配置(configuration)”按钮,打开该应用的URL映射规则页:

注意,这个对话框的上方列出了默认的ISAPI扩展名映射(每个URL扩展名都映射到了负责处理该扩展名的ISAPI)。对话框的下方列出了“通配应用映射(wildcard application map)”规则。我们可以点击“添加”按钮来给ASP.NET ISAPI添加一个应用的通配符映射,将其指向一个硬盘上的ASP.NET 2.0 ISAPI扩展:

非常重要的事项:确认你不选“核实该文件是存在的(Verify this file exists)”复选框。如果你不这么做的话,象WebResource.axd和其他被ASP.NET处理的但并没有对应物理文件的ASP.NET URL资源会停止工作,这会导致你的网页出错。

接下来,点击OK按钮接受变动,关闭所有的对话框。至此,你就配置了ASP.NET能在每个进入该应用的请求之前和之后运行编码。

对非ASP.NET资源启用表单认证和Url授权

一旦我们完成上面的步骤,把ASP.NET 2.0注册成进入我们IIS应用的所有URLs的通配符映射之后,我们就可以使用标准的ASP.NET认证和授权技术来鉴定我们应用中的用户,准许或拒绝他们的访问。

譬如,我们可以加一个web.config文件到我们的应用中,为该应用启用ASP.NET的表单认证特性,然后建立2个URL授权规则,拒绝“匿名”用户对test.asp和test.htm的访问:

<?xml version="1.0"?>

<configuration>

    
<system.web>
        
<authentication mode="Forms" />
    </
system.web>

    
<location path="test.asp">

        
<system.web>
            
<authorization>
                
<deny users="?"/>
                <
allow users="*"/>
            </
authorization>
        
</system.web>
        
    
</location>

    
<location path="test.htm">

        
<system.web>
            
<authorization>
                
<deny users="?"/>
                <
allow users="*"/>
            </
authorization>
        
</system.web>
        
    
</location>

</configuration>

现在,当我试图访问"test.asp"或者 "test.htm"时,ASP.NET认证和授权系统会先执行,检查我是否已经通过表单认证登录进当前的应用,如果还没有登录的话,会转向到该应用的login.aspx网页让我来登录:

注意,ASP.NET认证系统在上面使用的"ReturnUrl"已经被设置成"test.asp" url,这样在我登录之后,就会把我重新定向回去 (这跟.aspx网页的情形是一样的)。一旦我成功输入用户名/密码之后,我就能访问 test.asp 网页了:

因为上面的页面是一个经典的ASP文件,很明显地,我不能象在ASP.NET网页里那样使用User.Identity.Name属性来鉴别登录的用户。但是,我可以在经典的ASP网页里从"AUTH_USER"服务器变量中获取用户名(在把处理返回到经典的ASP ISAPI之前,ASP.NET会自动填充这个变量)。

在经典的ASP里使用这个的编码看上去是象下面这样子的:

<html>
    
<body>
        
<h1>Classic ASP Page</h1>

        
<h3>
            You are logged in as: 
            
<u>
                
<%=Request.ServerVariables("AUTH_USER") %>
            
</u>        
        
</h3>
    
</body>
</html>

点击这里下载实现了上述方案的完整样例应用。在默认情形下,它会使用SQL Express数据库储存ASP.NET 2.0的成员和角色管理数据。或者,你也可以创建和注册SQL 2000或SQL 2005数据库来储存成员和角色管理数据。我以前写的这个老的ASP.NET安全教材示范了该怎么做。

如何进一步了解ASP.NET安全

我强烈推荐你去买一本Stefan Schackow的精彩好书《ASP.NET 2.0 安全, 成员和角色管理(ASP.NET 2.0 Security, Membership, and Role Management)》。 Stefan是ASP.NET开发队伍中的主要成员,拥有和设计了ASP.NET 2.0版本中的安全特性。因此,他实实在在是了解他写出来的东西。

他书中的第六章题为“把ASP.NET安全与经典ASP集成(Integrating ASP.NET Security with Classic ASP)”,内中包含了我在上面示范过的方案的更加详细的细节,以及如何在其中使用角色安全,在ASP.NET 和 classic ASP之间来回传输数据等。

 

点击这里了解该书的详情或者在线购买。

其他在线ASP.NET安全资源

我在过去发表过许多讨论ASP.NET 2.0安全的ASP.NET 技巧, 诀窍, Recipes 和教程。下面是你也许想温习的几个重要帖子的列单:

想阅读更多的我写的免费ASP.NET 技巧, 诀窍, 和教程的话,请去看一下我的 ASP.NET 技巧, 诀窍, 和教程列表

希望本文对你有所帮助,

Scott

posted on 2007-03-05 13:22:00 by scottgu  评论(9) 阅读(11473)

免费的SQL Server培训录像(以及其他好的数据教程的指针)

【原文地址】Free SQL Server Training Videos (and other good data tutorial pointers)
【原文发表日期】 Thursday, March 01, 2007 9:54 PM

今天在 www.asp.net 网站上,我们发布了一个注重于SQL Server 2005 Express 的新的精彩的《“我怎么做?”》录像系列,你可以下载和使用,是完全免费的。

系列中包括了13个专业录像,从最基本的开始,然后探讨数据表/数据定义(schema) 的设计,使用数据类型,创建存储过程,使用报表服务和商业智能引擎,对你的数据启用全文搜索等等。我猜,即使是大多数富有经验的开发人员也许也能从报表服务和全文搜索录像中学到一些新东西呢:

你可以在这里免费下载或在线观看这些SQL培训录像。

万一你错过了我写的关于把 SQL Express 或 SQL Server数据库上传到远程主机环境中的帖子的话,我强烈建议你回去读一下这2个贴子:

去年夏天早先时候,我曾贴出过一个很受欢迎的贴子,里面提供了你可以下载,在你应用中使用的100多个数据库数据定义的例子:

最后,也许是最重要的,一定要去看一下Scott Mitchell 这里的极棒的ASP.NET 数据教程系列 ,内含超过50个无比精彩的ASP.NET中常见的数据场景的教程(同时提供了VB 和C# 编码):

希望本文对你有所帮助,

Scott

标签: , ,

posted on 2007-03-02 22:50:00 by scottgu  评论(5) 阅读(10010)

技巧/诀窍:在ASP.NET中重写URL

【原文地址】Tip/Trick: Url Rewriting with ASP.NET
【原文发表日期】 Monday, February 26, 2007 9:27 PM

经常有人请我指导应该如何动态地“重写”URL,以在他们的ASP.NETweb应用中发布比较干净的URL端点。这个博客帖子概述了几个方法,你可以用来在ASP.NET中干净地映射或重写URL,以及按照你自己的需求组织你的URL的结构。

为什么URL映射和重写很重要?

下面是开发人员想要对URL有更大的灵活性的最常见的场景:

1) 处理这样的情形:你要更改你的web应用中网页的结构,但你同时也要确保在你移动网页后,那些被人收藏的老URL不会成为死链接。重写URL允许你透明地将请求转交到新的网页地址而不出错。

2) 在象Google,Yahoo 和 Live 这样的搜索引擎中提高你网站上网页的搜索相关性。具体地来说,URL重写经常能使你在你网站上网页的URL里更加容易地嵌入关键词,这么做往往会增加别人点击你的链接的机会。从使用查询字符串参数到使用完全限定(fully qualified)的URL也能在某些情形下提高你在搜索引擎结果中的优先顺序。使用强制referring链接使用同样的大小写(same case)和URL入口(譬如,使用weblogs.asp.net/scottgu 而不是 weblogs.asp.net/scottgu/default.aspx)的技术也能避免因跨越多个URL而造成的网页排名(pagerank)的降低(avoid diluting your pagerank across multiple URLs),从而增加你的搜索结果。

在一个搜索引擎日渐驱动网站访问量的世界里,在你的网页排名上稍微得到一些提高就能给你的业务带来不错的投资回报(ROI)。逐渐地,这驱使开发人员使用URL重写以及其他SEO(搜索引擎优化 )技术来优化网站(注,SEO是个步调很快的空间,增加你的搜索相关性的建议月月在演变)。想了解一些关于搜索引擎优化方面好的建议的话,我建议你阅读一下《SSW Rules to Better Google Rankings (SSW的提高Google排名之要领)》,以及MarketPosition关于《how URLs can affect top search engine ranking (URL会如何影响顶级搜索引擎排名)》的文章。

例程的URL重写场景

为这个博客贴子起见,我将假设我们将在一个应用里建造一套电子商务的产品目录网页,产品是按种类来组织的(譬如,图书,录像,CD,DVD等等)。

让我们假定一开始我们有个网页叫Products.aspx,通过查询字符串参数接受一个类别名称,相应地过滤显示的产品。与这个Products.aspx网页对应类别的URL看上去象这样:

http://www.store.com/products.aspx?category=books
http://www.store.com/products.aspx?category=DVDs
http://www.store.com/products.aspx?category=CDs

但我们不想使用查询字符串来呈示每个类别,我们想修改应用,让每个产品类别对搜索引擎来说看上去象是一个独特的URL,并且在实际的URL中嵌入关键词(而不是通过查询字符串参数)。我们将在这个博客帖子剩下来的篇幅里,讨论一下达成这个目的我们可以采取的4种不同方法。

方法一:使用Request.PathInfo 参数而不是查询字符串

我将示范的第一个方法根本不使用URL重写,而是使用ASP.NET中不太为人所知的一个特性,Request的PathInfo属性。为帮助解释这个属性的有用之处,考虑一下我们电子商店下面这些URL的情形:

http://www.store.com/products.aspx/Books
http://www.store.com/products.aspx/DVDs
http://www.store.com/products.aspx/CDs

你会在上面这些URL中注意到的一个东西是,他们不再含有查询字符串值,取而代之的是,类别参数的值是附加到URL上的,是以Products.aspx网页处理器名称之后的/参数 值的方式出现的。然后,一个自动化的搜索引擎爬虫(search engine crawler)会把这些URL解释成三个不同的URL,而不是一个URL带有三个不同的输入值 (搜索引擎是不理会文件扩展名的,只把它当作URL中的另一个字符而已)。

你也许很想知道怎么在ASP.NET中处理这个附加的参数的情形。好消息是,这非常简单。只要使用Request的PathInfo属性就可以了,该属性返回URL中紧随 products.aspx 后面的那部分内容。所以,对上面这些URL, Request.PathInfo会分别返回 “/Books”, “/DVDs”,和 “/CDs”(万一你想知道的话, Request的Path 属性返回“/products.aspx” )。

然后,你可以轻易地编写一个函数来获取产品类别,象这样(下面这个函数去除前面的斜杠字符,只返回“Books”,“DVDs”,或 “CDs”):

    Function GetCategory() As String

        If 
(Request.PathInfo.Length 0Then
            Return 
""
        
Else
            Return 
Request.PathInfo.Substring(1)
        
End If

    End Function

样例下载:我建立的一个展示这个技术的样例应用可以在这里下载。这个样例和这个技术的很好的地方在于,为部署使用这个方法的ASP.NET应用,不需作任何服务器配置改动。在共享主机的环境里,这个技术也行之有效。

方法二:使用HttpModule实现URL重写

上述Request.PathInfo技术的替换方法是,利用ASP.NET提供的HttpContext.RewritePath方法。这个方法允许开发人员动态地重写收到的URL的处理路径,然后让ASP.NET使用刚重写过后的路径来继续执行请求。

譬如,我们可以选择向大众呈示下列URL:

http://www.store.com/products/Books.aspx
http://www.store.com/products/DVDs.aspx
http://www.store.com/products/CDs.aspx

在外界看来,网站上有三个单独的网页(对搜索爬虫而言,这看上去很棒)。通过使用 HttpContext的RewritePath方法,我们可以在这些请求刚进入服务器时,动态地把收到的URL重写成单个Products.aspx网页接受一个查询字符串的类别名称或者PathInfo参数。譬如,我们可以使用Global.asax中的Application_BeginRequest事件,来这么做:

    void Application_BeginRequest(object sender, EventArgs e) {

        
string fullOrigionalpath Request.Url.ToString();
        
        if 
(fullOrigionalpath.Contains("/Products/Books.aspx")) {
            Context.RewritePath(
"/Products.aspx?Category=Books");
        
}
        
else if (fullOrigionalpath.Contains("/Products/DVDs.aspx")) {
            Context.RewritePath(
"/Products.aspx?Category=DVDs");
        
}
    } 

手工编写象上面这样的编码的坏处是,很枯燥乏味,而且容易犯错。我建议你别自己写,而是使用网上现成的HttpModule来完成这项工作。这有几个你现在就可以下载和使用的免费的HttpModule:

这些模块允许你用声明的方式在你应用的web.config文件里表达匹配规则。譬如,在你应用的web.config文件里使用UrlRewriter.Net模块来把上面的那些URL映射到单个Products.aspx页上,我们只要把这个web.config文件添加到我们的应用里去就可以了(不用任何编码):

<?xml version="1.0"?>

<configuration>

  
<configSections>
    
<section name="rewriter"  
             requirePermission
="false" 
             type
="Intelligencia.UrlRewriter.Configuration.RewriterConfigurationSectionHandler, Intelligencia.UrlRewriter" />
  </
configSections>
  
  
<system.web>
      
    
<httpModules>
      
<add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule, Intelligencia.UrlRewriter"/>
    </
httpModules>
    
  
</system.web>

  
<rewriter>
    
<rewrite url="~/products/books.aspx" to="~/products.aspx?category=books" />
    <
rewrite url="~/products/CDs.aspx" to="~/products.aspx?category=CDs" />
    <
rewrite url="~/products/DVDs.aspx" to="~/products.aspx?category=DVDs" />
  </
rewriter>  
  
</configuration> 

上面的HttpModule URL重写模块还支持正则表达式和URL模式匹配(以避免你在web.config 文件里硬写每个URL)。所以,不用写死类别名称,你可以象下面这样重写匹配规则,把类别名称动态地从任何/products/[类别].aspx组合的URL里取出来:

  <rewriter>
    
<rewrite url="~/products/(.+).aspx" to="~/products.aspx?category=$1" />
  </rewriter>  

这使得你的编码极其干净,并且扩展性非常之好。

样例下载:我建立的一个使用UrlRewriter.Net模块展示这个技术的样例应用可以在这里下载

这个样例和这个技术的很好的地方在于,为部署使用这个方法的ASP.NET应用,不需作任何服务器配置改动。在设置为中等信任安全等级(medium trust)的共享主机的环境里,这个技术也行之有效 (只要把文件FTP/XCOPY到远程服务器就可以了,不需要安装)。

方法三:在IIS7中使用HttpModule 实现无扩展名的URL重写

上述的HttpModule方法在你要重写的URL含有.aspx 扩展名或者包含另一个被设置为ASP.NET处理的扩展名的情形下一切都工作。你这么做的话,不需要任何特定的服务器配置,你只要把你的应用拷贝到远程服务器,它会正常工作的。

但有的时候,你要重写的URL要么拥有一个不为ASP.NET处理的文件扩展名(譬如, .jpg, .gif, 或 .htm),要么根本没有扩展名。譬如,我们也许要把这些URL呈示成公开的产品目录网页(注意,它们没有 .aspx 扩展名):

http://www.store.com/products/Books
http://www.store.com/products/DVDs
http://www.store.com/products/CDs

在 IIS5 和 IIS6 中,使用ASP.NET处理上面这样的URL不是很容易。 IIS 5/6 使得在ISAPI扩展(ASP.NET就是这样一个扩展)里非常难以重写这些类型的URLS。你需要做的是使用ISAPI过滤器在IIS请求管道(request pipeline)的较早期实现重写。我将在下面的第四个方法里示范如何在 IIS5/6 实现这样的重写。

但好消息是, IIS 7.0使得处理这类情形容易之极。你现在可以在 IIS 请求管道的任何地方执行一个HttpModule,这意味着你可以使用上面的URLRewriter 模块 来处理和重写无扩展名的URL(甚至是带有 .asp,.php,或 .jsp 扩展名的URL)。下面示范了你在IIS7中该如何配置:

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

<configuration>

  
<configSections>
    
<section name="rewriter" 
             requirePermission
="false" 
             type
="Intelligencia.UrlRewriter.Configuration.RewriterConfigurationSectionHandler, Intelligencia.UrlRewriter" />
  </
configSections>
  
  
<system.web>
      
    
<httpModules>
      
<add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule, Intelligencia.UrlRewriter" />
    </
httpModules>
    
  
</system.web>

  
<system.webServer>

    
<modules runAllManagedModulesForAllRequests="true">
      
<add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule" />
    </
modules>

    
<validation validateIntegratedModeConfiguration="false" />

  </
system.webServer>

  
<rewriter>
    
<rewrite url="~/products/(.+)" to="~/products.aspx?category=$1" />
  </
rewriter>
  
</configuration>

注意一下<system.webServer>内<modules>部分设置为true的runAllManagedModulesForAllRequests属性。这个属性确保来自Intelligencia的UrlRewriter.Net模块(是在IIS7正式发布前编写的),会被调用,有机会重写到服务器的所有URL请求(包括文件夹)。上面的web.config文件非常酷之处在于:

1) 它在任何IIS7机器上都会工作,你不需要管理员在远程主机上启用任何东西,它也能在设置为中等信任安全等级(medium trust)的共享主机的环境场景下工作。

2) 因为我在<httpModules>和 IIS7 的<modules> 部分同时配置了UrlRewriter,我既能在 VS内置的web服务器(即Cassini)中,也能在IIS7下使用同样的URL重写规则。两者完全支持无扩展名的URL重写。这使得测试和开发非常容易。

IIS 7.0 将在今年的晚些时候作为Windows Longhorn服务器的一部分发布,将在几个星期内随Beta3版本的发布支持go-live许可。由于添加到IIS7中的所有的新宿主(hosting)特性,我们预期主机供应商将会非常快地开始积极提供IIS7账号,这意味着你应该很快就可以开始利用上述的无扩展名的URL重写支持。我们将在 IIS7 RTM 时段里发布一个为微软所支持的URL重写模块,该模板是免费的,你可以在IIS7上使用,并且这模块将对你web服务器上的所有内容的高级URL重写场景提供很好的支持。

样例下载:我建立的一个使用IIS7和UrlRewriter.Net模块展示无扩展名URL重写技术的样例应用可以在这里下载

方法四:在IIS5和IIS6中使用 ISAPIRewrite 来实现无扩展名的URL重写

如果你不想等到IIS7出来才利用无扩展名的URL重写,那么你最好的措施是使用ISAPI过滤器来重写URL。我知道有2个ISAPI过滤器方案,你也许要去看一下:

我没亲手用过上面的产品,虽然我听过到对这2个产品的好评。Scott HanselmanJeff Atwood 最近都写了精彩的博客贴子讲述使用这些产品的体验,同时提供了一些如何在这些产品中配置匹配规则的例子。Helicon Tech的ISAPI Rewrite的规则使用跟 Apache的mod_rewrite同样的句法,譬如(取自Jeff的博客贴子):

[ISAPI_Rewrite]
# fix missing slash on folders
# note, this assumes we have no folders with periods!
RewriteCond Host: (.*)
RewriteRule ([^.?]+[^.?/]) http\://$1$2/ [RP]

# remove index pages from URLs
RewriteRule (.*)/default.htm$ $1/ [I,RP]
RewriteRule (.*)/default.aspx$ $1/ [I,RP]
RewriteRule (.*)/index.htm$ $1/ [I,RP]
RewriteRule (.*)/index.html$ $1/ [I,RP]

# force proper www. prefix on all requests
RewriteCond %HTTP_HOST ^test\.com [I]
RewriteRule ^/(.*) http://www.test.com/$1 [RP]

# only allow whitelisted referers to hotlink images
RewriteCond Referer: (?!http://(?:www\.good\.com|www\.better\.com)).+
RewriteRule .*\.(?:gif|jpg|jpeg|png) /images/block.jpg [I,O]

一定要去读一下ScottJeff的贴子以了解这些ISAPI 模块的详情,以及你都能用它们做些什么。

注:使用ISAPI过滤器的一个坏处是,共享主机环境一般不允许你安装这样的组件,所以你要用它们的话,你要么需要一个专用的虚拟主机服务器,要么需要一个专用的主机服务器。但,如果你有一个主机计划允许你安装ISAPI的话,这会在IIS5/6下会提供最大的灵活性,让你过渡到IIS7推出为止。

在URL重写里处理ASP.NET PostBack

大家在使用ASP.NET和重写URL时经常遇到的一个疑难杂症跟处理postback场景有关。具体地来说,当你在一个网页上放置一个 <form runat="server"> 控件时,ASP.NET 会自动地默认输出标识的action属性指向当前所在页面。当使用URL重写时,会出现这样的问题,<form> 控件显示的URL不是原先请求的URL(譬如,/products/books),而是重写过后的URL(譬如,/products.aspx?category=books)。这意味着,当你做一个postback到服务器时,URL不再是你原先干净利落的那个了。

在 ASP.NET 1.0 和1.1 中,大家经常诉诸于继承<form> 控件生成他们自己的控件,来正确地输出要使用的action属性。虽然这可以工作,但结果有点乱,因为这意味着你需要更新你所有的页面来使用这个另外的表单控件,而且有时在Visual Studio所见即所得设计器里也会遇上问题。

好消息是,在ASP.NET 2.0中,有个比较干净的诀窍你可以用来重写<form>控件的action属性。具体地来说,你可利用新的ASP.NET 2.0控件适配器扩展架构来定制控件的输出,用你提供的值来覆盖action属性的值。这不要求在你的.aspx页面里做任何编码改动,而只要在你的/app_browsers文件夹里添加一个.browser文件,注册使用一个控件适配类即可输出新的action属性。

你可在这里查看一个我创建的样例实现,其展示了该如何实现与URL重写协作的表单控件适配器(Form Control Adapter) 。它在我上面使用的第一个(Request.PathInfo),第二个方法(UrlRewriter.Net 模块)中都工作,它使用Request的RawUrl属性获取原先没改写过的 URL来显示。而在第四个方法(ISAPIRewrite过滤器)中,你可以获取ISAPI过滤器保存在Request.ServerVariables["HTTP_X_REWRITE_URL"] 中的原先的URL值。

我上面的FormRewriter类实现在标准的ASP.NET和ASP.NET AJAX 1.0网页上应该都工作(如果你遇上问题的话,告诉我一声)。

正确地处理CSS和图像引用

不少人在第一次使用URL重写时,有时会遇上一个疑难杂症,就是他们发现他们的图像和CSS样式表引用有时会停止工作。这是因为他们在HTML网页里有对这些文件的相对引用,当你开始在应用里重写URL时,你需要意识到浏览器经常会在不同的逻辑层次结构层上(logical hierarchy levels)请求文件,而不是实际存储在服务器上的东西。

譬如,如果我们上面的/products.aspx网页对.aspx 网页里的logo.jpg有一个相对引用,但是通过 /products/books.aspx这个URL来请求的,那么浏览器在显示网页时,将会发出一个对/products/logo.jpg的请求,而不是对/logo.jpg的请求。要正确地引用这个文件,确认你用根目录限定了(root qualify)CSS和图像引用(“/style.css”,而不是 “style.css”)。对于ASP.NET控件,你也可以使用“~”句法从你应用的根目录来引用文件(譬如,<asp:image imageurl="~/images/logo.jpg" runat="server"/>) 。

希望本文对你有所帮助,

Scott

附注:想学习更多的ASP.NET 2.0技巧和诀窍的话,请查看一下我的ASP.NET 2.0技巧,诀窍和教程网页

附注2:特别感谢Scott Hanselman 和Michael K. Campbell在他们的网站测试我的表单控件适配器 (Form Control Adapter)。

posted on 2007-03-01 00:14:00 by scottgu  评论(51) 阅读(43562)