### ASP.NET：革命，不是革新

微软本来只需要对 Web 应用平台 ASP 作稍加改进，比方提供一个功能更强的服务器端脚本编程语言或者页面编译功能就可以保持或者甚至提高 ASP 的流行度。实际上 ASP.NET 提供了这两个功能--但是它还提供了更多的东西。简单的说，ASP.NET 是一场革命。在本文中，我将讨论 ASP.NET 对于 Web 编程所做的提高。
在 ASP 模型中，一个页面是通过 HTML 定义然后是通过嵌入的脚本扩展的。在 ASP.NET 中，不同的是，一个页面是一个生成 HTML 的类。这个模型被称为 WebForm。如果这个名字让你想起 VB6 的表单控件或者.Net WinForms，那么你摸到点边了。对于 WebForms 来说，Web 服务器控件都放在一个页面上。当这个页面被访问的时候，这些控件自动将它们以 HTML 的方式实现。浏览器和服务器之间的复杂过程对程序员来说是透明的，由服务器自动完成，并允许事件处理，就和 WinForms 一样。
欢迎页面
让我们马上开始创建一个叫 Greeter（欢迎者）的例子然后看看它是如何工作的。我在学习一个新技术的时候喜欢将它们拆开然后再凑成一整块。Greeter 仅仅是要求用户输入一个名字然后发出一条问候消息，但是它演示了 ASP.NET 的几个主要特点。
Greeter 页面定义在两个文件中：Greeter.aspx，在列表 A 中，和 Greeter.aspx.cs，在列表 B 中。.aspx 文件定义了页面的 UI（用户界面），而 C#文件（你也可以使用 Visual Basic.Net 或者 JScript.Net）在 GreeterLogic 类里实现了这个页面的逻辑。Greeter.aspx 文件第一行的@Page 指令通过继承将 UI 绑定在程序逻辑上。这个指令中的 ClassName 属性将 UI 类的名字指定为 CreeterUI。而 Inherits 属性指定 GreeterUI 是从 GreeterLogic 继承而来的。
Web 服务器端控件
在这个页面的<form>置标中申明了几个 Web 服务器端控件。Web 服务器端控件是标准 HTML 置标的 WebForm 抽象化。它们与标准的 HTML 相比能够提供事件处理并生成更多的功能。Web 服务器端控件在一个引用 asp 命名空间的 XML 置标里被申明。.aspx 文件中的每一个 Web 服务器控件在页面被执行的时候会导致控件的一个实例被创建。对每个实例的引用在其相应的基类中被保持。这便使得基类能够访问这些控件并能够让它用程序控制这些控件并处理这些控件产生的事件。每个 Web 服务器端控件指定属性 runat="server"来表明这个控件在 Web 服务器端运行以便为目标 Web 浏览器生成合适的 HTML。

最开始的三个 Web 服务器端控件是相当直观的：标签控件（label），文本框，和一个提交按纽。最后的一个控件，<asp:RequiredFieldValidator>，是不同的。一个验证控件检查另一个控件的状态并且当目标控件没有处于正确状态的时候会阻止这个表单被提交。在 Greeter 这个程序中，RequiredFieldValidator 控件被用来验证一个数据是否已经被输入到了文本框控件里。它并不关心这个数据的值是多少。初始状态下，RequiredFieldValidator 控件是不可见的。如果在按纽被单击的时候文本框是空的，那么就不会产生单击消息，而且 RequiredFieldValidator 控件会显示 ErrorMessage 属性的属性值，如图 B。对于高版本的浏览器来说，RequiredFieldValidator 控件生成 JavaScript 以在客户端进行数据验证。而对于低版本的浏览器来说则需要在服务器端进行验证。ASP.NET 除了 RequiredFieldValidator 控件外还提供了几个验证控件。

GreeterLogic 类
GreeterLogic 从类 System.Web.UI.Page 继承而来。它申明了几个 protected 数据域来保存对 Greeter.aspx 文件中创建的控件的引用。GreeterLogic 定义了两个方法。OnInit 方法重载了方法 Page.OnInit 来为 m_Button 单击事件添加一个 m_Button_Click 方法的代理。m_Button_Click 方法处理由 m_Button Web 服务器端控件生成的单击事件。这个单击事件触发一个到服务器端的数据发送过程。在服务器端，ASP.NET 重新生成这个页面然后恢复 WEb 服务器控件的状态。现在文本框的值就可以通过访问 TextBoxText 属性得到了。

配置 Greeter 程序
要配置 Greeter 应用程序，你可以遵循下面的步骤：
创建一个应用程序的目录。
在应用程序的目录里创建一个 bin 子目录。
把 Greeter.aspx 放在应用程序目录里。
使用命令 csc /t:library /out:Greeter.dll Greeter.aspx.cs 把 Greeter.aspx.cs 编译成一个库文件然后把 Greeter.dll 放在应用程序的 bin 子目录里。
创建一个指向应用程序目录的 IIS 虚拟目录。
.aspx 类的生成
ASP.NET 应用程序是由 IIS ISAPI 过滤器 aspnet_isapi.dll 处理的。在第一次访问的时候，ISAPI 过滤器在 ASP 命名空间下从 Greeter.aspx 生成一个.NET 类然后把它编译成一个二进制库文件。IIS 会自动搜索应用程序的 bin 子目录来寻找 GreeterLogic 基类。图 D 显示了生成的类的继承关系。生成的二进制文件会放在一个根据应用程序的虚拟目录而生成的一个存放临时 ASP.NET 文件的目录里。例如，如果 Greeter 的虚拟目录被命名被 IntroASPNET，那么 Greeter.aspx 可能会生成一个名字为 effla-hh.dll 的二进制文件，并且放在诸如 C:\WINNT\Microsoft.NET\Framework\v1.0.3328\Temporary ASP.NET Files\introaspnet\b5d8b0af\51648ab7 这样的一个目录里。
如果 Greeter.aspx 被修改了，那么 ISAPI 过滤器会在下一次页面访问的时候自动重新生成二进制文件。

ASP.NET 是实打实面向对象的
那么所有这些意味着什么呢？ASP.NET 提供了一个真正的面向对象的编程模型。WebForms 能够让页面的 UI 完全独立于页面的逻辑而被创建然后通过继承而结合起来。不会再有夹杂着 HTML 和 VBScript 的页面了。页面的逻辑是使用一种全功能的.NET 语言，比方 C#, Visual Basic.NET, 或者 Jscript.NET 实现的。更进一步的说，页面逻辑的实现过程可以随意使用所有的.NET 基类类库。Web 服务器控件封装了不同 Web 浏览器之间的区别并允许你从更高的级别来设计 Web 页面。所有的一切都会被编译成微软中间语言（MSIL）并且能够在运行时同步转化成机器码。不会再有解释性页面中存在的那种性能损失了。如果这还不能叫做一种革命的话，我就真的需要一本新的字典了:-)。
<html>
<head>
<title>Greeter.aspx</title>
</head>
<body>
<form id="QueryName" method="post" runat="server">
<!-- Heading -->
<asp:Label id="m_Heading" runat="server" Font-Bold="true" Font-Size="16pt" Text="Please enter your name:">
</asp:Label>
<br>
<!-- Input controls -->
<asp:Label id="m_Label" runat="server" Font-Bold="True" Text="Name:">
</asp:Label>
<asp:TextBox id="m_TextBox" runat="server" Width="152px"></asp:TextBox>
<asp:Button id="m_Button" runat="server" Text="Get Greeting"></asp:Button>
<br><!-- Input validation -->
<asp:RequiredFieldValidator id="m_Validator" runat="server" ControlToValidate="m_TextBox" ErrorMessage="You haven't entered your name!" ForeColor="Red">

</asp:RequiredFieldValidator>
</form>
</body>
</html>
using System;namespace TechRepublic
{
public class GreeterLogic : System.Web.UI.Page
{ // Protected data members: // - Server controls created by the .aspx derived class.
protected System.Web.UI.WebControls.Label m_Heading;
protected System.Web.UI.WebControls.Label m_Label;
protected System.Web.UI.WebControls.TextBox m_TextBox;
protected System.Web.UI.WebControls.Button m_Button;
protected System.Web.UI.WebControls.RequiredFieldValidator m_Validator; // Initialize the page.
override protected void OnInit( EventArgs e )
{ // Attach an event handler for the GetGreeting button click.
this.m_Button.Click += new System.EventHandler( this.m_Button_Click ); // Initialize the base class.
base.OnInit( e );
} // Handle the "Get Greeting" button click.
private void m_Button_Click( object sender, System.EventArgs e )
{ // Set the greeting in the heading.
m_Heading.Text = "Greetings " + m_TextBox.Text + "!"; // Hide the label, text box and button.
m_Label.Visible = false;
m_TextBox.Visible = false;
m_Button.Visible = false;
}
}
}