第2章 第一个ASP.NET应用的单文件版本
第1章实现的第一个ASP.NET应用示例,展示了集成开发工具开发ASP.NET应用的全貌。读者可能也体会到,无需对ASP.NET框架本身作深入了解,利用Visual Studio.NET就可以开发出很好的ASP.NET应用。虽然笔者鼓励大家尽量用Visual Studio.NET开发ASP.NET应用,但在某些情况下,譬如手头没有集成开发工具或希望着手更多的事情以加深对ASP.NET的理解时,编写单文件版本的ASP.NET应用可能是个好注意。单文件版本的ASP.NET应用中的代码和界面混在同一个.aspx文件中,在第一次被请求时,它被动态编译成程序集。这样对于开发人员,就不用显式地调用相应编程语言的编译器编译隐藏代码文件了。
本章主要内容如下:
● Web窗体的基本语法
● 客户端引发服务器端事件的底层实现
● 使用跟踪排除错误
● 单文件版本的ASP.NET应用开发步骤
2.1 预备知识
为了顺利编写单文件版本的ASP.NET应用,读者需要了解基本的窗体语法知识。
提示
这里仅提供有关窗体语法的一个基本介绍,进一步的介绍读者可以参考本书第3章。
先看一个非常简单的.aspx文件,该文件返回“Hello”给客户:
[Visual Basic] <%@ Page language="vb" %> <%-- 演示基于单文件的ASP.NET页面 --%> <% Response.Write("<p>Hello") %> [C#] <%@ Page language="C#" %> <%-- 演示基于单文件的ASP.NET页面 --%> <% Response.Write("<p>Hello"); %>
第一行代码是@ Page指令。利用@ Page指令的Language属性设置页面的编程语言。因为该指令在服务器端执行,所以被包围在<%和%>之前。
第二行为服务器端的注释语句。
第三行用<%声明一段服务端代码块的开始。
第四行利用Response.Write方法向客户端写回“Hello”字符串。“<p>”表示一个段落标志。
第五行用%>声明一段服务端代码块的结束。
对于Response.Write方法,存在一种简化的方式,可以直接把其输出放在“<%=”和“%>”直接,例如:
[Visual Basic] <%@ Page language="vb" %> <% dim I as Integer For I=0 to 5 %> <font size="<%=i%>"> <p>Hello World! </font> <% Next %> [C#] <%@ Page language="c#" %> <% for(int i=0;i<6;i++) { %> <font size="<%=i%>"> <p>Hello World! </font> <% } %>
执行结果如图2-1所示。
图2-1 执行结果
与Active Server Pages (ASP)不同,在ASP.NET的代码块(在<%和%>标志之间)中不能声明函数或子例程。函数或子例程只能在<Script>和</Script>标志之间定义。例如下面的代码演示了例程Hello的定义及其使用:
[Visual Basic] <%@ Page language="vb" %> <Script runat="server"> '定义例程 sub sayHello() Response.Write("<p>Hello") end sub </Script> <%-- 调用例程 --%> <% sayHello() %> [C#] <%@ Page language="c#" %> <Script runat="server"> //定义例程 void sayHello() { Response.Write("<p>Hello"); } </Script> <%-- 调用例程 --%> <% sayHello(); %>
上述页面向客户返回Hello。
上面的示例表明,在<Script></Script>代码块中使用注释的方法跟在代码隐藏文件中使用注释的方法相同。一定要把Script的runat属性设置为server,否则代码将按默认设置在客户端运行。
提示
请读者一定要弄清楚代码运行的位置,客户端代码的编程主要跟文档对象模型,并且其语言为解释型的脚本语言。而服务器端的代码可以充分利用服务器端的各种资源,编程语言为编译型的语言,并且可以动态发出客户端代码,例如:
[Visual Basic] <%@ Page language="vb" %> <Script runat="server"> '定义例程 sub sayHello() Response.Write(" <Script language=JScript> ") Response.Write("document.write('<p>Client Hello');") Response.Write("</" & "Script>") end sub </Script> <% sayHello() %> [C#] <%@ Page language="C#" %> <Script runat="server"> //定义例程 void sayHello() { Response.Write(" <Script language=JScript> "); Response.Write("document.write('<p>Client Hello');"); Response.Write("</" + "Script>"); } </Script> <% sayHello(); %>
在服务器端编写的客户端代码可以直接放到客户端脚本块中。上面的代码可以修改为:
[Visual Basic] <%@ Page language="vb" %> <Script language=JScript> document.write('<p>Client Hello'); </Script> [C#] <%@ Page language="c#" %> <Script language=JScript> document.write('<p>Client Hello'); </Script>
修改后的代码显得更好读一些。
提示
对于.aspx文件内的HTML标志,ASP.NET框架不会保持不变,只对特定的ASPX标志进行处理。
要想顺利地设计出.aspx文件,还要掌握如何往Web窗体页中添加Web服务器控件和HTTP控件。Web服务器控件在服务器端运行,它不直接对应到HTML元素,而是在请求时被动态呈现为合适的HTML元素。一个HTTM控件相当于一个HTML标志元素,例如HTML Button控件相当于Type属性为“button”的INPUT HTML元素:
<INPUT type="button" >
还可以继续指定该INPUT元素的其他属性,例如Value、OnClick等,而这些属性由客户端的浏览器直接解释:
<INPUT type="button" onclick="this.value='client clicked'" value="Button">
但是,ASP.NET框架为HTML标志增添了runat属性,该属性默认为“client”,可以通过将runat设置为server,把HTML控件转变为服务器端的控件。这时允许在服务器端的脚本中添加跟该控件相关的代码。例如下面的代码在单击HTML Button控件后,在服务器端激活OnServerClick事件:
[Visual Basic] <%@ Page language="vb" %> <Script runat="server"> sub Button1_ServerClick(sender as object,e as System.EventArgs) Button1.Value="Server Clicked" end sub </Script> <html> <title>演示HTML服务器端控件</title> <body> <form runat="server"> <INPUT type="button" OnServerClick="Button1_ServerClick" value="Click Me!" id="Button1" runat="server"> </form> </body> </html> [C#] <%@ Page language="cs" %> <Script runat="server"> void Button1_ServerClick(object sender, System.EventArgs e) { Button1.Value="Server Clicked"; } </Script> <html> <title>演示HTML服务器端控件</title> <body> <form runat="server"> <INPUT type="button" OnServerClick="Button1_ServerClick" value="Click Me!" id="Button1" runat="server"> </form> </body> </html>
从浏览器请求该网页,将显示标题为“Click Me!”的按钮,单击该按钮后,按钮的标题修改为“Server Cliicked”。
读者可能对上述执行结果表示不解,客户端引发的事件怎么就神不知鬼不觉地传到了服务器端呢?服务器端又如何区分是哪个控件引发的事件呢?为了回答这个问题,我们看看该.aspx文件产生的回应HTML文件:
<html> <title>演示HTML服务器端控件</title> <body> <form name="_ctl0" method="post" action="demo6.aspx" id="_ctl0"> <input type="hidden" name="__EVENTTARGET" value="" /> <input type="hidden" name="__EVENTARGUMENT" value="" /> <input type="hidden" name="__VIEWSTATE" value="dDwxOTI0MjI4NTEyO3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDE+Oz47bDx0PHA8bDx2YWx1Z Ts+O2w8U2VydmVyIENsaWNrZWQ7Pj47Oz47Pj47Pj47PlHYIMOTlQB7/IiD17MuoycLy603" /> <Script language="javaScript"> <!-- function __doPostBack(eventTarget, eventArgument) { var theform = document._ctl0; theform.__EVENTTARGET.value = eventTarget; theform.__EVENTARGUMENT.value = eventArgument; theform.submit(); } // --> </Script> <input language="javaScript" onclick="__doPostBack('Button1','')" name="Button1" id="Button1" type="button" value="Server Clicked" /> </form> </body> </html>
提示
展开浏览器的【查看】菜单,单击【源文件】将打开记事本显示上述内容。
下面我将对产生的HTML文件做仔细说明,以帮助读者弄明服务器如何处理客户端引发的事件。
首先查看<form>元素,在.aspx文件中,通过下面的代码添加了一个<form>元素(也就是一个HTML控件):
<form runat="server"> … </form>
因为指定在Server端运行,所以ASP.NET将对该标志进行翻译,翻译的结果包括:
● 设定Form元素的相关属性,例如name、id、action等属性。
● 添加3个隐藏域:一个隐藏域用以保存引发表单被发送的HTML控件,另一个隐藏域用以保存事件参数,还有一个用以保存跟服务器端控件状态数据,这个隐藏域在服务端被称为状态视图对象。前两个隐藏域是服务器端能够处理客户端事件的关键之一。
.aspx文件中的HTML按钮控件也是一个服务器端控件,所以ASP.NET框架也要负责把它翻译成合适的HTML代码,翻译后的结果包括:
● 设置了name属性。
● 设置了按钮的客户端脚本语言属性(language属性)。
● 重写了OnClick属性(该属性是在客户单击按钮后执行的代码)。OnClick以按钮的name属性值为第一个参数调用客户端的函数__doPostBack。__doPostBack根据传入的两个参数设置隐藏域__EVENTTARGET和隐藏域__EVENTARGUMENT的值,然后通过表单的submit方法向服务器回发网页。
提示
上面的讨论进一步说明了服务器端的HTML控件的特点,它们总是会在服务器端进行适当的处理后,才返回给客户,而客户端的HTML控件仅是一个HTML标志,ASP.NET框架不做任何处理而直接将其返回。
服务器接收到回发的请求后,通过检查表单的隐藏域__EVENTTARGET获得引发回发事件的HTML控件的name,从隐藏域__EVENTARGUMENT获得跟事件处理相关的数据,接下来,ASP.NET根据name查找相应的对象,并以事件处理参数调用该对象相应的事件处理代码。
从上面的讨论可知,为了在服务器端处理服务器控件的事件,必须满足如下要求:
● 该控件必须在服务器端运行。
● 该控件必须包含在一个服务器端运行的HTML Form控件内。
● 该控件的name属性必须惟一的设置。
提示
可以指定服务器端控件的name属性,但在ASP.NET呈现该控件时,总是会根据该控件的UniqueID属性重新设置客户端HTML元素的name属性,以确保其惟一性。
我们将在本书的后续章节中对服务器端事件处理的深入讨论。现在读者只要知道可以编写服务端控件(包括服务端的HTML控件和Web控件)的事件处理代码,这些事件在客户端引发,但在服务器上处理,ASP.NET框架通过两个隐藏域传递跟引发事件的控件和相关事件参数。
对HTML控件,我们还想指出很重要的一点,那就是仍然可能指定客户端的事件响应代码。
[Visual Basic] <%@ Page language="vb" %> <Script runat="server"> sub Button1_ServerClick(sender as object,e as System.EventArgs) Button1.Value="Server Clicked" end sub </Script> <html> <title>演示HTML服务器端控件</title> <body> <form runat="server"> <INPUT type="button" OnServerClick="Button1_ServerClick" onmouseover="this.style.backgroundColor='yellow'" onmouseout="this.style.backgroundColor='lightgreen'" style="font: 8pt verdana; background-color:lightgreen; border-color:black; height:30; width:200" id="Button1" runat="server"> </form> </body> </html> [C#] <%@ Page language="cs" %> <Script runat="server"> void Button1_ServerClick(object sender, System.EventArgs e) { Button1.Value="Server Clicked"; } </Script> <html> <title>演示HTML服务器端控件</title> <body> <form runat="server"> <INPUT type="button" OnServerClick="Button1_ServerClick" onmouseover="this.style.backgroundColor='yellow'" onmouseout="this.style.backgroundColor='lightgreen'" style="font: 8pt verdana; background-color:lightgreen; border-color:black; height:30; width:200" id="Button1" runat="server"> </form> </body> </html>
提示
ASP.NET框架只支持有限个事件的服务器端的处理,例如单击事件等。因为服务器端上的事件处理会涉及到一次客户到服务器的一个往返行程,这样对一些高频的鼠标事件,例如onmouseover,放到客户端处理更合适。
如果该控件单击将引发表单回发,那么客户端处理OnClick事件要小心,因为ASP.NET会自动把一段提交表单的客户端脚本附加到OnClick中。在客户端的OnClick看起来应为下面的形式:
OnClick="客户端代码;"
这样ASP.NET自动产生的客户端脚本会自动附加到“客户端代码;”后不会产生错误。
注意
一定不要把“客户端代码;”中的“;”给遗漏了,否则将报告客户端脚本有误。
下面将介绍Web控件。跟HTML控件相比,Web控件只能是服务器端控件,因此必须把Web控件的runat属性设置为server。另外,声明Web控件时必须在类型前加上“asp:”命名空间,例如:
<asp:Button id="Button3" runat="server" Text="Web 按钮控件" ToolTip="我是 Web 按钮控件 "></asp:Button>
提示
命名空间用以保证命名的惟一,同时也是代码的逻辑组织形式。
如果需要,还可以设置Web控件的更多属性。
注意
Web控件的属性跟HTML控件的属性不同,它们用以标志服务器端Web控件自身的状态,ASP.NET框架会根据自身的状态绘产生合适的HTML代码。例如上面的ASPX代码将产生如下的HTML代码:
<input type="submit" name="Button3" value="Web按钮控件" id="Button3" title="我是 Web按钮控件" />
从翻译可以看出,Button Web按钮控件被翻译为一个类型为“submit”的HTML Input元素,控件的ToolTip属性被翻译为提交按钮的Title属性。
跟服务器端的HTML控件类似,也可以在服务器端处理Web控件的事件,而该事件是在客户端引发的,例如:
[Visual Basic] <%@ Page language="vb" %> <Script runat="server"> shared clickcount as Integer=0 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) clickcount+=1 Response.Write(clickcount) End Sub </Script> <html> <title>演示Web控件</title> <body> <form runat="server"> <asp:Button id="Button1" OnClick="Button1_Click" runat="server" Text="Web按钮控件" ToolTip="我是Web按钮控件"/> </form> </body> </html> [C#] <%@ Page language="cs" %> <Script runat="server"> static int clickcount =0; private void Button1_Click(object sender, System.EventArgs e) { clickcount++; Response.Write(clickcount); } </Script> <html> <title>演示Web控件</title> <body> <form runat="server"> <asp:Button id="Button1" OnClick="Button1_Click" runat="server" Text="Web按钮控件" ToolTip="我是Web按钮控件"/> </form> </body> </html>
上述代码的执行结果如图2-2所示。这时单击窗体上的按钮,数字将不断增大。
图2-2 执行结果
说明
(1)示例代码中的第三行声明一个静态变量(也就是为.aspx页面对应的Web窗体类添加了一个静态成员)。注意,不能在<%…%>之间声明静态成员,只能在服务端的脚本块中声明。
(2)通过把事件处理的方法直接赋值给Web控件的“On事件”属性,可以在服务器端处理服务控件的事件。
除此之外,下面的代码也能正确地处理鼠标的单击事件:
[Visual Basic] <%@ Page language="vb" %> <Script runat="server"> shared clickcount as Integer=0 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) clickcount+=1 Response.Write(clickcount) End Sub Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) AddHandler Button1.Click ,addressof me.Button1_Click End Sub </Script> <html> <title>演示Web控件</title> <body> <form runat="server"> <asp:Button id="Button1" runat="server" Text="Web按钮控件" ToolTip="我是Web按钮控件"/> </form> </body> </html>
说明
Visual Basic.NET中用Addhandler方法将特定的处理方法和控件的事件关联。关联的实质是实例化一个委托并将之赋值给控件的某个事件(不要忘了,在.NET中,事件就是一个委托变量!)
[C#] <%@ Page language="cs" %> <Script runat="server"> static int clickcount=0 ; private void Button1_Click(object sender, System.EventArgs e) { clickcount++; Response.Write(clickcount); } private void Page_Init(Object sender , System.EventArgs e) { Button1.Click+=new System.EventHandler(Button1_Click); } </Script> <html> <title>演示Web控件</title> <body> <form runat="server"> <asp:Button id="Button1" runat="server" Text="Web按钮控件" ToolTip="我是Web按钮控件"/> </form> </body> </html>
说明
C#用“+=”把控件事件和处理代码挂钩。请注意,默认情况下Web窗体的AutoEventWireup属性为True,这时,页面的Init事件发生时,会自动调用Page_Init方法。如果指定页面的AutoEventWireup的属性为False,那么必须如下修改代码:
[Visual Basic] <%@ Page language="vb" AutoEventWireup="false"%> … Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init AddHandler Button1.Click ,addressof me.Button1_Click End Sub 评注:Visual Basic.NET允许用Handlers关键字将处理代码和事件直接挂钩。 [C#] <%@ Page language="cs" AutoEventWireup="false"%> … override protected void OnInit(EventArgs e) { Button1.Click+=new System.EventHandler(Button1_Click); }
说明
当Web页面被创建时,Web窗体的Init事件被引发。引发后实际上是调用OnInit方法,OnInit方法又会通过代理调用具体的事件处理代码。
2.2 排除错误
因为缺少智能代码提示,编写单文件的ASP.NET应用时发生错误更是难免。不过ASP.NET框架本身为排除.aspx文件中的编码错误提供了很好的支持。动态编译.aspx文件时发生编译错误,都会返回给调用者,并给出排除错误的建议。
请建立具有下面内容的.aspx文件:
[Visual Basic] <%@Page language="vb"AutoEventWireup="true"%> <Script runat="server"> Private Sub Page_Load(ByVal sender As System.Object,ByVal e As System.EventArgs) if not IsPostBack then Label1.Text=System.Now.ToString("f") end if End Sub </Script> <html> <title>演示排除错误</title> <body> <form runat="server"> <asp:Label id="Label1" runat=server/> </form> </body> </html> [C#] <%@ Page language="cs" AutoEventWireup="true"%> <Script runat="server"> private void Page_Load(object sender, System.EventArgs e) { // 在此处放置用户代码以初始化页面 if(! IsPostBack) { Label1.Text=System.Now.ToString("f"); } } </Script> <html> <title>演示排除错误</title> <body> <form runat="server"> <asp:Label id="Label1" runat=server/> </form> </body> </html>
上述代码的本意是在标签上显示服务器上的当前时间。但是Now应是System.DateTime对象的属性,而不是System的属性,这样当在浏览器上请求该窗体时,将获得编译错误信息,如图2-3所示。
图2-3 编译错误提示信息
提示
在图2-3中单击【显示详细的编译器输出】按钮,可以了解.aspx文件被动态编译的细节,譬如引用了哪些程序集,动态生成的程序集保存的位置等。在图2-3中单击【显示完整的编译源】按钮,还可以查看ASP.NET根据.aspx文件动态产生的源代码文件,如图2-4所示。
图2-4 根据.aspx文件动态产生的源代码
从图2-4可以清楚地看出,.aspx文件被转化为一个System.Web.UI.Page派生类。
图2-4中的错误提示信息指出了发生错误的源代码所在的位置,给出了解决该错误的建议方法,这样排除错误就很容易了。用System.DateTime.Now替换“System.Now”,就修正了错误,然后刷新请求,将会出现如图2-5所示的正确结果。
图2-5 排除错误后获得正确结果
提示
单文件窗体的一个好处是修改.aspx文件后,不用编译,再次请求该页,就能看到更新的结果。
排除错误的另外一个可用的强大工具就是ASP.NET跟踪,通过将@Page指令的Trace属性设置为true就可以启用跟踪:
[Visual Basic] <%@ Page language="vb" AutoEventWireup="true" Trace="true"%> [C#] <%@ Page language="cs" AutoEventWireup="true" Trace="true"%>
启用跟踪后,ASP.NET在响应每个请求时,会详细列出下面的信息:
● 请求详细信息
包括会话ID、请求类型、请求的时间、状态代码、请求编码和响应编码,某次请求的详细信息如图2-6所示。
图2-6 请求详细信息
● 跟踪信息
所有Trace.Write方法输出的信息都在这儿列出,ASP.NET框架预先在Page_Init等方法中加入了Trace.Write调用输出以输出跟踪信息,如图2-7所示。
图2-7 跟踪信息
● 控件树
详细列出了Web窗体页面上的各种控件及父子关系,如图2-8所示。
图2-8 控件树
● 标头集合
列出了服务器返回给客户的Header(标头)信息,如图2-9所示。
图2-9 标头集合
● 服务器变量
列出服务器端的所有变量,如图2-10所示。
图2-10 服务器变量
上述信息能帮助开发人员全面地了解在一次Web窗体请求过程中所发生的事情的所有细节,当然也是排除错误的绝佳辅助工具。
另外,通过调用Trace对象的Write方法,还能将自定义的消息写到跟踪消息栏中,例如把下面的代码插入到.aspx文件的Page_Load方法中。
[Visual Basic] <%@ Page language="vb" Trace="true"%> <Script runat="server"> Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Trace.Write("页面事件", "调试-Page_Load") End Sub </Script> <html> <title>演示Trace.Write</title> </html> [C#] <%@ Page language="cs" Trace="true"%> <Script runat="server"> private void Page_Load(object sender, System.EventArgs e) { Trace.Write("页面事件", "调试-Page_Load"); } </Script> <html> <title>演示Trace.Write</title> </html>
在浏览器中请求上述内容的.aspx文件,执行结果如图2-11所示。黑体字突出显示的代码的输出在图2-11中用方框圈住。
图2-11 执行结果
如果错误排除了,需要取消跟踪,那么将@Page指令的Trace属性设置为false即可。
提示
在集成开发环境下,调试一个ASP.NET应用跟调试其他.NET应用没什么不同,在隐藏代码文件中设置断点,然后按【F5】键启动调试。但如果要调试但文件版本中的脚本,可以用.NET框架提供的CLR调试器DbgClr.exe,该调试器位于.NET框架安装的GuiDebug目录下。本书后面将详细介绍用DbgClr.exe调试.aspx页面的技术。
2.3 实现步骤
有了前面的准备知识,下面我们将实现第一个ASP.NET应用的单文件版本的实现步骤。
2.3.1 第一步:建立Web应用目录
在d:盘上新建一个demoasp目录,然后到【控制面板】上双击【管理工具】,双击【Internet信息服务】图标,激活IIS管理工具。
右键单击【默认Web站点】按钮,指向【新建】,在弹出的子菜单中单击【虚拟目录】按钮,如图2-12所示,将激活新建虚拟目录向导。
图2-12 利用关联菜单新建虚拟目录
按照向导提示,依次设置虚拟目录的别名(如图2-13所示)、对应的磁盘目录(如图2-14所示,本例中设置为d:\demoasp)和虚拟目录的访问权限(如图2-15所示)。
图2-13 设置虚拟目录别名
图2-14 设置虚拟目录对应的磁盘目录
图2-15 设置虚拟目录的访问权限
提示
一个Web应用对应着一个IIS虚拟目录,虚拟目录的别名可以跟对应的磁盘目录名不一致。客户通过虚拟目录别名访问Web应用。假设服务器diana上有别名为demoASP的虚拟目录对应到磁盘目录d:\demo,而磁盘目录demo下有文件default.aspx,那么为了访问该文件,可以在浏览器请求地址栏中的URL为http://diana/demoASP/default.aspx。
2.3.2 第二步:创建登录的.aspx文件
文件版本的登录窗体文件为login.aspx,位于d:\demoasp目录下,其内容如下:
[Visual Basic] <%@ Page Language="vb" %> <Script runat=server> Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) If FormsAuthentication.Authenticate(username.Text, pwd.Value) Then FormsAuthentication.RedirectFromLoginPage(username.Text, True) Else Response.Redirect("login.aspx") End If End Sub </Script> <HTML> <HEAD> <title>请输入用户名和口令WebForm1</title> </HEAD> <body > <form runat="server"> <table border=0 align=center width=400> <tr> <td width=150> <asp:label id="Label1" runat="server">用户名</asp:label> </td> <td width=200> <asp:TextBox id="username" runat="server"></asp:TextBox> </td> <td width=50> <asp:RequiredFieldValidator id="RequiredFieldValidator1" runat= "server" ErrorMessage="用户名不能为空" ControlToValidate="username" Display="Dynamic">* </asp:RequiredFieldValidator> </td> </tr> <tr> <td> <asp:label id="Label2" runat="server">口令</asp:label> </td> <td> <INPUT id="pwd" type="password"name="Password1"runat="server"> </td> <td> <asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" ErrorMessage="用户名不能为空" ControlToValidate="pwd" Display="Dynamic"> *</asp:RequiredFieldValidator> </td> </tr> <tr> <td></td> <td align=center> <asp:Button id="Button1" runat="server" Text=" 提 交 " OnClick="Button1_Click"></asp:Button> </td> <td></td> </tr> <tr> <td></td> <td width=400> <asp:ValidationSummary id="ValidationSummary1" runat="serv ></asp:ValidationSummary> </td> <td?</td> </tr> </table> </form> </body> </HTML> [C#] <%@ Page Language="cs" %> <Script runat=server> private void Button1_Click(object sender, System.EventArgs e) { if( FormsAuthentication.Authenticate(username.Text, pwd.Value)) { FormsAuthentication.RedirectFromLoginPage(username.Text, true); } else Response.Redirect("login.aspx"); } </Script>
其他代码跟编程语言为Visual Basic时相同,省略…
提示
利用<table></table>元素帮助对其控件。<tr></tr>表示定义一行,<td></td>表示定义一列。
继续创建默认的窗体default.aspx,其实现代码如下所示:
[Visual Basic] <%@ Page Language="vb" %> <Script runat=server> Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) If Not IsPostBack Then Label1.Text=User.Identity.Name&",欢迎光临" End If End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) FormsAuthentication.SignOut() End Sub </Script> <HTML> <HEAD> <title>请输入用户名和口令WebForm1</title> </HEAD> <body > <form runat="server"> <table border=0 align=center width=400> <td width=300> <asp:Label id="Label1" runat="server" >欢迎光临</asp:Label> </td> <td> <asp:Button id="Button1" runat="server" Text="Sign Out" OnClick="Button1_Click"></asp:Button> </td> </table> </form> </body> </HTML> [C#] <%@ Page Language="cs" %> <Script runat=server> private void Page_Load(object sender, System.EventArgs e) { if(!IsPostBack) Label1.Text = User.Identity.Name + ",欢迎光临"; } private void Button1_Click(object sender, System.EventArgs e) { FormsAuthentication.SignOut(); } </Script>
下面的代码跟语言为Visual Basic时相同。
2.3.3 第三步:编写配置文件
在demoasp目录下新建一个web.config文件,其内容如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> <authentication mode="Forms"> <forms loginURL="login.aspx"> <credentials passwordFormat = "Clear" > <user name="小杨" password="5157"/> <user name="晓华" password="1234"/> </credentials> </forms> </authentication> <authorization> <deny users="?,晓华"/> <allow users="*" /> </authorization> </system.web> </configuration>
配置文件中的第一行XML序言不可省略,否则将报告配置文件中无效的字符,如图2-16所示。
图2-16 配置错误
2.3.4 第四步:测试
直接在浏览器下输入http://diana/demoasp/default.aspx会自动导入到登录画面。在登录画面中的用户名中输入“小杨”,口令中输入“5157”,再单击【提交】按钮则会成功导入default.aspx画面。如果登录画面输入了其他用户,则不需离开登录画面而直接进入default.aspx画面。
2.3.5 第五步:部署
把login.aspx、default.aspx和web.config文件拷贝到目标服务器某个虚拟目录对应的磁盘目录下,就完成了在发行服务器上的部署。这个过程中没有涉及到任何程序集。
提示
如果.aspx窗体用到了自动编译过程中没有自动添加对其引用的程序集,则应该把这些程序集也复制到目标服务器Web应用的bin子目录下。
2.3.6 第六步:开发单文件版本的ASP.NET应用
如果已有Visual Studio.NET工具,则可以用它来简化单文件窗体的设计。按照下面介绍的方法,可以把一个具有隐藏代码文件的窗体轻松地转化为一个单文件窗体。
● 新建一个Web窗体,新建的窗体为Webform3。
● 利用Web窗体设计器设计窗体,设计好的Web窗体如图2-17所示。
图2-17 设计好的Web窗体
设计好的界面最上边是一个广告控件AdRotatorl,下面是一个HTML表格Table控件。表格的第一行依次为HTML标签控件、Web文本框控件和Web必须域验证控件,它验证的控件是文本框;表格的第二行中间是Web按钮控件;表格第三行是Web验证汇总控件。
● 编写代码
一般可以为界面元素添加事件处理的代码,譬如添加“Go!”按钮的单击事件的响应,或添加一些公共的方法:
[Visual Basic] Sub PageSub(ByVal para As Object) End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click '添加处理事件单击的代码 End Sub [C#] void PageSub(object para) { } private void Button1_Click(object sender, System.EventArgs e) { }
提示
除此之外,隐藏代码文件还有Web窗体设计器自动产生的代码,以及在新建一个Web窗体时自动产生的代码。
● 实施转化
修改.aspx文件的第一行,修改后的代码看起来应如下所示:
[Visual Basic] <%@ Page language="vb" AutoEventWireup="false" %> [C#] <%@ Page language="c#" AutoEventWireup="false" %>
然后在.aspx文件的第三行新加如下的代码块:
<Script runat=server> </Script>
接下来,把隐藏代码文件中的实现Web页类的代码拷贝到<Script></Script>代码块内,注意,那些跟界面元素相关的变量声明,以及类本身的声明不用拷贝。若编程语言为Visual Basic,则要对拷贝后的代码做进一步的处理,即修改跟处理界面元素事件相关的代码:
(1)去掉事件处理代码方法声明中的Handers关键字及其以后的代码,例如,把:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
修改为:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
(2)在Page_Init方法的最后,手工将事件处理代码和界面控件的事件挂钩,例如在Page_Init的最后添加下面的代码:
AddHandler Button1.Click, AddressOf Button1_Click
根据隐藏代码合成的单文件Web窗体文件最后看起来如下所示:
[Visual Basic] <%@ Page Language="vb" AutoEventWireup="false" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <!--下面的代码直接从隐藏代码中拷贝而来 --> <Script runat="server"> #Region"Web 窗体设计器生成的代码 " '该调用是 Web 窗体设计器所必需的。 <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() End Sub Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init 'CODEGEN: 此方法调用是 Web 窗体设计器所必需的。 '不要使用代码编辑器修改它。 InitializeComponent() '添加的代码,手工挂钩事件 AddHandler Button1.Click, AddressOf Button1_Click End Sub #End Region Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load '在此处放置初始化页的用户代码 End Sub Sub PageSub(ByVal para As Object) End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) '添加处理事件单击的代码 End Sub </Script> <HTML> <HEAD> <title>WebForm3</title> <meta name="GENERATOR"content="Microsoft Visual Studio.NET 7.0"> <meta name="CODE_LANGUAGE"content="Visual Basic 7.0"> <meta name="vs_defaultClientScript"content="JavaScript"> <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5"> </HEAD> <body MS_POSITIONING="GridLayout"> <form id="Form1"method="post"runat="server"> <FONT face="宋体"> <TABLE id="Table2" style="Z-INDEX: 101; LEFT: 50px; WIDTH: 300px; POSITION: absolute; TOP: 94px; HEIGHT: 86px" cellSpacing="1" cellPadding="1" width="300" border="0"> <TR> <TD style="WIDTH:152px;HEIGHT:30px"> <DIV style="DISPLAY: inline; WIDTH: 146px; HEIGHT: 31px"ms_positioning="FlowLayout">输入查询条件</DIV> </TD> <TD style="HEIGHT:30px"> <asp:TextBox id="TextBox1" runat="server"></asp:TextBox></TD> <TD style="HEIGHT:30px"> <asp:RequiredFieldValidator id="RequiredFieldValidator1" runat="server" ErrorMessage=" 查 询 条 件 不 能 为 空 " ControlToValidate="TextBox1">* </asp:RequiredFieldValidator></TD> </TR> <TR> <TD style="WIDTH:152px"></TD> <TD> <asp:Button id="Button1" runat="server" Text="Go!" Width="121px"></asp:Button></TD> <TD></TD> </TR> <TR> <TD style="WIDTH:152px"></TD> <TD> <asp:ValidationSummary id="ValidationSummary1" runat="server"></asp:ValidationSummary></TD> <TD></TD> </TR> </TABLE> <asp:AdRotator id="AdRotator1" style="Z-INDEX: 102; LEFT: 32px; POSITION: absolute; TOP: 11px" runat="server" Width="425px" Height="60px" AdvertisementFile="XMLFile1.xml"BackColor="CornflowerBlue"></asp:AdRotator> </FONT> </form> </body> </HTML> [C#] <%@ Page language="c#" AutoEventWireup="false" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <!--下面的代码直接从隐藏代码中拷贝而来 --> <Script runat=server > private void Page_Load(object sender,System.EventArgs e) { // 在此处放置用户代码以初始化页面 } #region Web Form Designer generated code override protected void OnInit(EventArgs e) { // //CODEGEN:该调用是 ASP.NET Web 窗体设计器所必需的。 // InitializeComponent(); base.OnInit(e); } ///<summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 ///</summary> private void InitializeComponent() { this.Button1.Click+=new System.EventHandler(this.Button1_Click); this.Load+=new System.EventHandler(this.Page_Load); } #endregion void PageSub(object para) { } private void Button1_Click(object sender,System.EventArgs e) { } </Script> <HTML> <HEAD> <title>WebForm3</title> <meta name="GENERATOR"Content="Microsoft Visual Studio 7.0"> <meta name="CODE_LANGUAGE"Content="C#"> <meta name="vs_defaultClientScript"content="JavaScript"> <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5"> </HEAD> <body MS_POSITIONING="GridLayout"> <FORM id="Form1"method="post"runat="server"> <FONT face="宋体"> <asp:AdRotator id="AdRotator1" style="Z-INDEX: 102; LEFT: 41px; POSITION: absolute; TOP: 11px" runat="server" Width="425px" AdvertisementFile="XMLFile1.xml" Height="60px"BackColor="CornflowerBlue"></asp:AdRotator> <TABLE id="Table2" style="Z-INDEX: 103; LEFT: 53px; WIDTH: 300px; POSITION: absolute; TOP: 108px; HEIGHT: 86px" cellSpacing="1" cellPadding="1" width="300" border="0"> <TR> <TD style="WIDTH:152px;HEIGHT:30px"> <DIV style="DISPLAY: inline; WIDTH: 146px; HEIGHT: 31px"ms_positioning="FlowLayout">输入查询条件</DIV> </TD> <TD style="HEIGHT:30px"> <asp:TextBox id="TextBox1" runat="server"></asp:TextBox></TD> <TD style="HEIGHT:30px"> <asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" ControlToValidate="TextBox1" ErrorMessage=" 查 询 条 件 不 能 为 空 ">*</asp:RequiredFieldValidator></TD> </TR> <TR> <TD style="WIDTH:152px"></TD> <TD> <asp:Button id="Button1" runat="server" Width="121px" Text="Go!"></asp:Button></TD> <TD></TD> </TR> <TR> <TD style="WIDTH:152px"></TD> <TD> <asp:ValidationSummary id="ValidationSummary1" runat="server"></asp:ValidationSummary></TD> <TD></TD> </TR> </TABLE> </FONT> </FORM> </body> </HTML>
好了,借助于Visual Studio.NET,不需要对ASP.NET语法有多少了解,我们就有了设计任意复杂的单文件窗体的能力。当然,为了更好地设计Web窗体,对ASP.NET语法的深入了解是必要的,本书后续章节将在本章的基础上,进一步介绍ASP.NET语法。
2.4 小结
本章介绍了开发单文件版本的ASP.NET应用的全貌。开发单文件版本的ASP.NET应用需要更多地了解ASP.NET的语法,并可能会产生更多的错误。本章提供了ASP.NET语法的综述性的介绍,并提供了排除ASP.NET应用中BUG的方法。本章的重点仍然是第一个ASP.NET应用的单文件版本的实现步骤。本章最后还介绍了如何利用Visual Studio.NET来开发单文件版本的ASP.NET应用。