任务4. 4 网上书店在线人数和访问人次的统计
②在用户单击购买目前没有存货的图书时,弹出消息框给出提示信息:
③在原来的窗口显示一个新页面(在客户端执行):
④向浏览器输出文件,FileName指向浏览器输出文件的文件名。
2)网页转向
Response对象的Redirect方法能将浏览器重定向到其他的URL地址,即跳转到其他网页。重定向可以带参数(称为查询字符串),若使用查询字符串首先要在地址URL后加“?”,参数使用键值对的方式,多个参数用“&”符号分割,完整格式为:URL?键=值&…&键=值。这些参数会被跳转后的目标页面接收,下面是一些实例:
①不带查询字符串的跳转
②带查询字符串的跳转
3)向浏览器输出Cookie的方法将在本项目介绍Cookie对象时讲解。
(2)Request对象
Request对象的主要功能是从服务器端获取客户端(浏览器)提交的信息,Request对象能获得从浏览器表单传递的参数,获得客户端的Cookie中的信息,还能获得URL地址中查询字符串中的信息。Request对象是一个内部对象,能无需申明而直接使用,它同样是Page对象的成员之一。
Request对象的常用属性和方法见表4.3。
表4.3 Request对象常用的属性
1)使用Request获取表单数据
注意表单的提交方式要设置为“Post”,否则可能获取不到数据,与Get方法相比较,使用Post方法能够提交到服务器端的数据量更大。例如,提取表单中的用户名信息。其中,user-Name是表单中录入用户名的输入框的ID号。
2)使用Request获取地址栏查询字符串中的参数数据
Request对象的QuerySting属性和Request.Params属性可以获取查询字符串的键值对信息。注意此时的提交方式必须为“Get”。否则可能获取不到数据。
①提取查询字符串中的信息,book_id为查询字符串键值对中的键名:
②也可以使用Params属性获取:
3)使用统一的形式
ASP.NET为获取上述浏览器参数信息提供了统一的方法,不管是获取表单信息还是获取地址中的查询字符串,都可使用简化形式:Request[“表单元素ID/查询字符串键名”]。将前述实例改写如下:
4)获取客户端Cookie信息的方法将在本项目介绍Cookie对象时讲解。
【任务实施】
登录框的实现步骤。
①项目2任务1的登录页面如图4.1所示。
图4.1登录页面示例
②登录页的代码文件中,登录按钮的事件处理方法btn_Login_Click()的已有代码如下:
③在登录页的同目录下生成一个新的Web窗体,取名为BackMain.aspx,作为模拟登录后进入的后台功能页面,在该页面上添加一个标签控件,取名为lb_user,用于显示从登录页获取的用户名。然后,在该页面的代码文件的Page_Load事件处理函数中添加如下代码:
protected void Page_Load(object sender,EventArgs e)
{
//从地址栏参数中得到从登录页传递过来的用户名,user_id为键名
String strUser= Request["user_id"];
//在标签控件中显示用户名
This. lb_user.Text= strUser;
}
【拓展与提高】
(1)Web窗体(Web Form)的概念
Web窗体由页面文件和代码文件两部分组成,页面文件的后缀名为.aspx,代码文件为完整的页面文件名+后缀名.cs。Web窗体的这种形式体现了网页的表现部分和逻辑部分相分离的原则。
1)页面文件
每一个Web窗体都有一个可视化的页面,在VS.NET开发环境的设计视图中,设计者可以从工具箱中向页面拖入各种Web控件以构成用户界面,还可以在选中控件后,在对应的属性窗口中为Web控件设置属性。
2)代码文件
ASP.NET技术采用Code-Behind(代码后置)技术组织动态网页,使程序逻辑代码能独立从动态网页的呈现部分分离出来,这是由.NET FrameWork框架自动实现的,这样就使ASP.NET动态网页的结构更加清晰。
(2)ASP.NET网站项目文件和目录集合
ASP.NET网站程序在VS.NET开发平台中以项目的形式存在,每一个网站项目包括如下一些特点的文件和目录,见表4.4。
表4.4网站项目的主要文件和目录
(3)Page对象
1)Page对象概述
Page对象表示从ASP.NETWeb服务器请求的一个Web窗体,Web窗体由视觉元素(HTML、Web控件)和逻辑代码(C#代码)两部分组成,可视元素部分存储为一个后缀名为.aspx的网页文件,经编译后发送到客户端,由浏览器解释执行;逻辑代码存储为一个单独的类文件,后缀名为.aspx.cs,经编译后形成后缀名为.dll的动态链接库文件。当从客户端接受用户请求后,该.dll文件就会在服务器端运行,并动态响应请求创建输出发送回浏览器。如果.aspx网页文件包含Web控件,Web控件的实例将在运行时被动态创建。
当创建新的Web窗体时,将从Page基类派生新类。例如,从System.Web.UI.Page派生一个新类,系统将自动生成如下代码:
aspx文件在用户请求该页时动态地进行编译,它与类文件的关联关系通过该页顶部的脚本指令来建立。如下面代码所示,其中,Codebehind表示所关联的类文件的名称。
2)Page对象的属性、方法和事件
①Page对象的属性
Page类有多个属性、方法和事件,下面将对主要的属性、方法和事件进行介绍。
a.IsPostBack属性:该属性指明网页是否存在提交回传Web服务器,数据类型为bool,为True表示存在回传,反之则表示不存在回传。当初始请求网页或Web服务器接收客户端请求,并回传客户端时,都会创建新的网页实例,并触发网页的Load加载事件,因此,通常情况下可以在Page对象的Page_Load事件处理方法中判断网页是初次请求还是提交回传,在Page_Load事件处理方法中使用IsPostBack属性代码通常如下:
b.Controls属性:表示网页中Web控件对象的集合,通常可以使用Controls属性来遍历网页中的所有控件。
c.pageLayout属性:指定网页的布局形式,可指定为GridLayout或FlowLayout,分别代表网格布局和流布局。
d.bgColor属性:指定网页的背景颜色。ASP.NET中使用RGB值表示颜色,格式为#号后带6个16进制数,每两位依次表示三基色红、绿、蓝的成分比重,例如,#ffffff代表白色,而#000000代表黑色。
e.background属性:指定网页的背景图片来源地址,使用URL虚拟地址。一般情况下,网站在规划时会将网站所使用到的各种资源(包括图片资源)集中分类放置在一个或几个目录下,这样便于通过引用相对路径以使用该资源。
f. text属性:指定网页内文本的颜色。
g. title属性:指定网页的标题,将显示在浏览器的标题栏中。
除了可以在类文件中通过编程使用这些属性,也可以在属性窗口中对这些属性进行设置,但请注意属性窗口的目标下拉框的目标必须指定为“DOCUMENT”。
②Page对象的方法
DataBind方法:该方法将数据源(DataSource)绑定到页面中所有的Web控件,一旦调用了Page类的DataBind方法后,就不必再为页面中每个Web控件调用它们各自的DataBind方法了。很多Web控件都可以指定数据源,通常数据源来自已被外部数据填充后的数据集的数据表或数据视图。
③Page对象的事件
a.Page_Init事件:在网页初始化时被触发,不包括提交回传时,此时网页尚未加载,不能对控件进行操作。
b.Page_Load事件:在网页加载时被触发。
c.PreRender事件:在网页呈现前被触发。
d.DataBinding事件:在计算Web控件的数据绑定表达式时被触发。当数据源修改后,需要进行重新绑定Web控件,这时就会重新计算控件的数据绑定表达式。
e.Page_Unload事件:在网页卸载时被触发。
f.Disposed事件:在网页对象销毁时被触发。
(4)ASP.NET网站运行流程
Web应用程序运行于Web服务器,浏览器对服务器提交请求时,Web服务器会自动调用Web应用程序进行处理,并会重新生成HTML文档,将处理结果嵌入其中返回给浏览器,浏览器解释执行返回的HTML文档并呈现给用户。Web服务器将HTML文档发送至客户端后,即会把将此HTML文档丢弃,当该网页被再次请求时,Web服务器又会重新生成该HTML页面。.NET运行框架生成网页对象被封装成Page对象,缓存于服务器内存中。网站运行流程包括以下几个阶段:
①页面初始化
网页对象的Page_Init事件将被触发,且网页对象(包括其内部的Web控件)的状态信息将被回存,这些状态信息被封装成ViewState对象,在内部以“键/值对”的方式保存网页对象及其控件的状态信息。
②页面加载
网页对象的Page_load事件将被触发。通过网页对象的IsPostBack属性,可以检查该网页对象是初次加载还是提交回传后导致的重新加载,如果是提交回传导致的网页加载,则会读取与回存提交前网页的状态信息以保持和先前网页(此网页对象在一次回传后已被销毁)的一致性。
③事件回传
客户端页面若有提交请求发生,首先会调用页面验证控件的Validate方法,对所要验证的Web控件中的用户输入数据进行验证。若页面用户输入没有全部通过验证,则不提交并给出提示信息;然后将检查提交控件的AutoPostBack属性,只有当值为true时才将事件提交至服务器处理。
④事件处理
当服务器接收到事件通知,则会去执行对应的事件处理方法,一般会去修改部分的页面状态数据,未修改的部分通过回存ViewState页面状态对象的方式保持与先前页面的一致性。
⑤页面返回
网页对象的Render事件将被触发,.NET框架将构建一个HtmlTextWrite对象,使用它重新生成新页面的HTML代码,新的网页HTML代码会被返回到客户端由浏览器呈现,这一回传—处理—返回的过程可称为一个环回。在环回过程中,页面状态信息也会被回存。因此,页面状态能在环回后保持一致。
⑥清理
在此阶段,网页对象将被移除,网页对象Page_Unload事件将被触发,通常可以完成一些最后的状态还原与清理工作,例如关闭文件、关闭数据库连接、移除对象等,特别需要注意的是,系统资源必须在此释放,这一点对保证服务器有效工作特别重要。否则资源会一直被占据直到下一次的垃圾回收进程发生为止。
【教学评价】
评价标准见表4.5。
表4.5任务4.1评价标准
任务4. 2 局部刷新用户名验证
【任务描述】
在网上书店系统注册账号时,不管是使用邮件地址作为账号还是使用用户名作为账号,都存在账号可能重复的情况,如果账号重复,系统会在页面上提示该账号已经被使用,并要求重新输入新账号,如图4.2所示。
图4.2网上书店注册账号验证和提示
在这种情况下,账号信息需要提交到服务器端进行后台验证,如果使用客户端与服务器端的传统交互操作方法,则整个注册页面将被刷新,会产生网页闪烁的现象,在网络状况不佳的情况下,甚至会出现停滞现象。此时,用户并不知道发生了提交回传,在没有预期的情况下,导致用户体验不好,
ASP.NET 3.5提供了基于Ajax技术的Ajax Web控件,与传统的页面提交回传时刷新整个页面不同,Ajax能够实现页面的“局部刷新”,使用Ajax进行提交回传时,用户感受不到页面在刷新,Ajax适合提交小数据量的信息,正是这个原因导致Ajax提交回传的速度更快。在任务实施之前,首先对Ajax技术的概念和ASP.NET提供的几个重要的Ajax Web控件进行介绍。
【知识准备】
(1)AJAX概述
典型的传统Web操作过程为:浏览者操作一个Web页面,并使用表单向服务器提交信息,这时浏览器会向服务器发送请求,服务器接受请求并执行相应处理后,将重新生成一个结果页面回传给浏览者,这一过程会导致页面刷新。由于每次交互操作都需要进行请求和回传,而服务器接受请求和返回处理结果的过程依赖于服务器的响应速度,而浏览者提交前的页面和服务器处理后的回传页面在形式上基本相同,因此很多提交回传的信息都不是有效信息。
为了解决这一问题,通过在浏览器和服务器之间设计一个AJAX中间层,改变传统的Web应用客户端和服务器的请求—回传的模式,通过使用SOAP或其他基于XML技术的Web Service接口,并在客户端采用JavaScript处理来自服务器的响应,实现浏览器与服务器的交互。使用AJAX不会产生典型的回传,实现了局部刷新的效果,从而提升了用户体验,减少了网络带宽。
AJAXWeb应用主要优点是,无需进行整个页面的回传就能进行局部的更新,这样能使Web服务器尽快响应用户的请求,因而提升了用户体验。另外,由于AJAX客户端处理采用JavaScript,因此无须安装任何插件,并能执行一些简单处理,如格式验证,从而分担了服务器端事务处理的负担;Web服务器端采用Web Service技术,同样也无需安装第三方附件。
AJAX也有一些小的不足,如无法维持“历史”状态,会破坏浏览器功能中的“后退”功能;实际交互操作已经在客户端和浏览器间发生了,而页面并没有显式的刷新现象,用户通过现象掌握交互操作的进度,因此需要在页面明显的位置提示交互操作进度。
(2)AJAX基础
AJAX其实并不是一项新技术,而是一些老技术的混合,通过将这些技术进行一定的提炼、整合和发扬,形成AJAX技术。这些老技术包括:
XHTML:基于XHTML1.0规范的、增强的超文本标记语言。
CSS:基于CSS2.0的层叠样式表。
DOM:包括HTML DOM、XML DOM等的文档对象模型。
JavaScript(JS):JavaScript客户端编程脚本语言。
XML:包括XML DOM、XSLT、XPath子技术的可扩展标记语言。
上述这些技术早已在现有的Web应用开发中广泛使用,如果你还不能全部了解这些技术,请务必上网查询相关资料和教程,因为它们都是Web开发的重要基础。
AJAX技术的核心是XMLHttpRequest对象,该对象是一个JavaScript对象,正是该对象实现了AJAX可以在服务器和浏览器之间通过JavaScript创建一个中间层,从而实现了异步通信,如图4.3所示。
图4.3 XMLHttpRequest对象实现过程
使用AJAX技术时,当用户填写表单后,数据并不是直接从客户端发送到服务器,而是通过客户端发送到一个称为AJAX引擎的中间层。AJAX引擎将数据发送到Web服务器,服务器同样会将处理结果返回给AJAX引擎,并由AJAX引擎将结果返回给浏览器。在编程时,AJAX引擎被封装成XMLHttpRequest对象,开发人员无需知道AJAX引擎如何将数据发送给服务器以及如何从服务器接收数据。只需使用JS调用XMLHttpRequest对象即可实现浏览器端与服务器的局部更新式的交互操作。总之,AJAX编程是通过使用XHTML、CSS、JS、DOM、XML等技术实现的,这些技术以下列方式支持AJAX的具体实现:
使用XHTML+CSS进行页面外观显示;
使用DOM进行动态显示和交互;
使用XML和XSLT进行数据交换;
使用XMLHttpRequest进行异步数据查询、检索;
使用JS代码调用XMLHttpRequest对象,获取页面数据。
(3)ASP.NET AJAX控件
AJAX交互操作的原始实现是使用JS+ XMLHttpRequest编程,而ASP.NET技术自3.5版本就开始提供ASP.NET AJAX控件。AJAX控件直接构建了AJAX的开发环境,大大方便了AJAX应用程序的开发,在VS.NET 2008的工具箱的“AJAX Extentions”栏目下能找到所有的ASP.NET AJAX控件,下面首先介绍本任务将会用到的两个最为重要的AJAX控件。
1)脚本管理控件(ScriptManger)
ScriptManger控件能够对整个页面的局部更新进行管理,它能够生成相关的代理脚本以便通过JS访问Web Service。ScriptManger控件只能在一个页面中使用一次,即是每个页面只能有一个ScriptManger控件。
HTML代码如下:
常用属性如下:
AllowCustom ErrorRedirect:指明在异步回发过程中是否进行自定义错误重定向;
AsyncPostBackTimeout:指定异步回发的超时事件,默认为90 s;
EnablePageMethods:是否启用页面方法,默认值为false;
EnablePartialRendering:在支持的浏览器上为UpdatePanel控件启用异步回发。默认值为True;
LoadScriptsBeforeUI:指定在浏览器中呈现用户界面前是否加载脚本;
ScriptMode:指定可加载的脚本类型,默认为Auto。
ScriptManger控件通常需要同其他AJAX控件搭配使用,一般无需配置就能够直接使用,它像一个代理人,只在内部管理和调度浏览器与服务器端的AJAX交互操作,而不执行具体的事务。
2)更新区域控件(UpdatePanel)
UpdatePanel控件是最常用的AJAX控件,它是一个容器控件,使用方法与Panel控件类似,放入该容器中的其他ASP.NETWeb控件就能够实现局部刷新。当进行AJAX交互操作时,整个页面中只有UpdatePanel控件内的Web控件或事件会被刷新,页面的其他区域则不会被刷新。UpdatePanel控件用来创建局部更新区域,无需编写脚本程序,直接使用该控件就能实现局部更新。
HTML代码如下:
常用属性如下:
RenderMode:指明UpdatePanel控件内呈现的标记应为<div>或<span>;
ChildrenAsTriggers:指明UpdatePanel容器控件内的子控件的提交回传是否会导致该控件及其内部子控件的更新,默认值为True;
EnableViewState:指明控件是否自动保存提交回传过程中的页面状态数据;
Triggers:指明导致UpdatePanel控件进行更新的触发器的集合;
UpdateMode:指明UpdatePanel控件是在每次处理事件后自动更新还是使用它的Update方法进行手动更新;
Visible:UpdatePanel控件是否可见。
UpdatePanel控件必须依赖ScriptManage控件才能实现局部动态更新。UpdatePanel控件以异步方式发送到服务器,服务器接受请求进行处理后,通过回传DOM对象进行异步局部更新。
UpdatePanel控件HTML标记内部还包括下一级的ContentTemplate标签,开发人员可以将任何ASP.NETWeb控件放置到ContentTemplate标签内,放入ContentTemplate内的Web控件就能够实现局部更新操作,将一个文本框和一个按钮控件放入后的HMTL代码如下:
UpdatePanel控件内还包括Triggers标签,Triggers标签内包含AsyncPostBackTrigger元素。AsyncPostBackTrigger用来指明将某个服务器端控件,及其触发的事件作为UpdatePanel异步更新的触发器,因此要配置作为异步更新触发器的Web控件的ID和事件名称,将上例中的文本框的TextChanged事件作为异步更新触发器的代码如下:
Triggers标签内还可以包含PostBackTrigger元素,该元素指定UpdatePanel内的某个控件使用传统的提交回发方式进行交互操作。当使用PostBackTrigger标签制订某个控件后,该控件触发事件并进行回传后,页面不会异步更新,而会进行传统的整页刷新,指定文本框控件进行传统刷新的代码如下:
【任务实施】
任务实施步骤
①创建网上书店用户注册页面,首先放置一个ScriptManger控件到页面,然后如图4.4所示制作布局表格,在第一行(邮箱/手机号码注册行)放置一个UpdatePanel控件,在UpdatePanel控件内部放置一个文本控件,ID属性设置为tbEmail,在其后放置一个CustomValidator用户自定义验证控件,采用默认ID属性值。其余部分用户界面的设计如图4.4所示。
图4.4网上书店注册页面的Email验证
②选中UpdatePanel控件,在属性面板中,单击Triggers属性后部的浏览按钮,打开Triggers集合编辑器,添加一个新项(该项为异步更新项),在右侧的行为栏设置ControlID属性的值为tbEmail,设置EventName属性的值为TextChanged(该事件并非是文本框中文本发生改变时触发,而是文本框失去光标后触发),即是说,当tbEmail文本框失去光标后,会触发UpdatePanel的异步更新。
③选中tbEmail文本框控件,在属性面板中设置AutoPostBack为True,以保证文本框的TextChanged能触发异步提交回传。单击闪电符号切换到事件面板,为TextChanged事件映射事件处理方法tbEmail_TextChanged。
④选中tbEmail文本框控件后的CustomValidator用户自定义验证控件,设置Text属性为空,设置ErrorMessage属性为“该邮箱已经被注册,请更换其他邮箱。”,当CustomValidator控件验证失败时,就会将该信息直接显示在文本框控件之后。
⑤切换到用户注册页面的代码文件,在tbEmail_TextChanged事件处理方法中添加如下代码:
⑥单击VS.NET 2008工具栏上的绿色箭头按钮或按快捷键“F5”,启动调试运行。当在用户注册页面输入邮箱号后,光标移动到下一个文本框中时,如果该邮箱没有注册,则不显示任何提示信息;如果该邮箱已被注册,则会在输入邮箱号的文本框后显示“该邮箱已经被注册,请更换其他邮箱。”的出错提示信息。而且在整个过程中,页面没有出现明显的刷新现象,浏览器状态栏上的进度条也没有出现进度提示。
【拓展与提高】
下面介绍其他一些常用的ASP.NET AJAX控件。
(1)脚本管理代理控件(ScriptMangerProxy)
ScriptManger脚本管理控件作为整个页面的管理者,能够提供强大的功能以至开发人员无需关心它是如何实现AJAX脚本程序的。但是,一个页面只允许使用一个ScriptManger控件,如果存在多个ScriptManger控件则会产生异常,为了解决这个问题,可以使用另一个脚本管理控件——ScriptMangerProxy控件。
ScriptMangerProxy控件与ScriptManger控件的功能和使用方式都非常相似,而ScriptMangerProxy控件在同一页面可以使用多次,并可以和ScriptManger控件一起使用。例如,如果Web应用程序中使用母版页时,当母版页和内容页都需要使用到AJAX交互操作时,使用ScriptMangerProxy控件就能在母版页和内容页中同时实现局部更新。
(2)定时控件(Timer)
Timer控件能够在一定时间的间隔内触发某个事件。Timer控件在Winform应用程序中很常见。而在Web应用中,Timer控件定时触发事件的特点决定了使用它将消耗大量系统资源。如果Timer控件作为一般的ASP.NETWeb控件实现,那么每次事件触发都会产生典型的提交回传过程,将消耗大量的网络带宽和服务器资源。为克服这一缺点,可考虑使用非典型回传的AJAX局部更新技术实现Time控件。因此,ASP.NET AJAX中提供了一个Timer控件,能够控制应用程序定时触发事件进行局部更新。
HTML代码如下:
常用属性如下:
Enabled:是否启用Tick时间引发。
Interval:设置Tick事件之间的连续时间,单位为ms。
通过配置Timer控件的Interval属性,能够指定Time控件定时进行事件处理,下例实现了页面计时器的功能:页面上放置了一个ScriptManage控件和一个UpdatePanel控件。在UpdatePanel控件内部,放置了一个Label控件和一个Timer控件:Timer控件设置为每隔1 000 ms (Timer控件定时的单位为ms)触发Tick事件,执行Timer1_Tick事件处理方法;Label控件用于显示时间。
(3)更新进度控件(UpdateProgress)
ASP.NET AJAX无明显的刷新现象有时也会使用户不清楚系统执行操作的进展和当前状态,以致会误认为系统没有响应而执行重复操作,甚至产生一些非法操作。UpdateProgress控件可用于解决该问题,当服务器端与客户端进行异步请求—应答时,UpdateProgress控件提示当前任务执行的进度,因而提升用户的体验,减少了用户误操作的可能性。
HTML代码如下:
需要说明的是:如果服务器和客户端之间的交互操作时间较长,则UpdateProgress控件的进度提示信息会出现;如果服务器和客户端之间交互的时间很短,则基本上看不到UpdateProgress控件的提示信息显示。
下例放置一个UpdateProgress控件提示后台操作的进度,同时放置了一个Label控件和一个Button控件,当Button控件被单击会执行后台时间处理方法,如果耗时较长,UpdateProgress控件位置则会显示提示信息,页面HTML代码如下:
在Button按钮的事件处理方法中,首先使用了System.Threading.Thread.Sleep方法指定系统线程休眠的时间为3 000 ms,3 000 ms后在页面上显示当前时间,代码如下:
当用户单击按钮后,3 s时间内页面都会显示“正在操作中,请稍后…”的提示信息,3 s后显示当前时间,运行效果如图4.5所示.
图4.5 UpdateProgress更新进度控件实例
【教学评价】
评价标准见表4.6。
表4.6任务4.2评价标准
任务4. 3 购物车的实现
【任务描述】
大多数的电子商务网站都提供了网上购物功能,购物车是电子商务网站购物功能模块的一个核心部分。网上书店系统也不例外,用户在多个图书展示页面选购了商品后,网上书店系统会自动将用户选购的图书商品信息进行汇总,计算数量和价格,并以表格的方式显示在统一的“购物车”页面,如图4.6所示。
图4.6购物车页面
在网上书店系统中,某一时刻存在多个用户购买商品,而每个用户所购买的商品是不同的。因此,每个当前用户都应该分配一个购物车,即服务器应该为每一个客户端分配一个独立的空间用于存储信息,这种对于某个客户端的独立信息称之为会话(Session)信息,而将某一个客户端与服务器建立连接,进行交互操作,直至连接断开的这一过程称为一个会话。使用ASP.NET提供的Session会话对象可以存储会话信息,Session对象称之为会话级的状态信息存储对象,其内部存储的信息可以在同一客户端用户浏览的多个页面间实现共享,Session对象的这一特点可以用于实现购物车,下面首先对Session对象进行介绍。
【知识准备】
Session会话对象
1)Session对象概述
Session对象可以保存与当前用户会话的相关信息,该信息只能被当前用户所使用。在Web应用中,每一个客户端用户都有自己的Session对象,用于为每个用户存储各自的信息,一般可以将特定客户端用户需要在各页面共享的数据信息保存于Session中。Session对象对于特定用户具有唯一性,即是说,对于Web应用而言,不同客户端用户访问到的Session对象的内容是各不相同的。Session对象是HttpSessionState类的实例,由系统内部自动创建,可以直接访问。
当一个客户端用户初次请求Web应用程序的某个页面时,Web应用程序就会为该用户建立一个Session对象。该Session对象将一直存在于Web服务器内存中,直到客户端用户不再请求服务。Web服务器通过检测是否有提交操作判断客户端的状态,如果客户端在一个时间内没有任何操作,Web服务器就认为该客户端不再需要提供服务,就会自动断开与该客户端的连接,此时该客户端对应的Session对象就会被销毁和回收,这就是Session对象的生命周期,称之为会话期。从检测到客户端无操作到Session对象失效的时间段就称为Session的过期时间,该时间默认设置为20 min,可以通过Session对象的TimeOut属性设置Session的过期时间,该时间设置得越长,则Web服务器为客户端提供的等待服务时间越长,Session对象的驻留时间就越长,相应的,服务器资源占用的时间就越长。
最直接读写Session的方法是使用它的键/值对,键(Key)为字符串类型,值(Value)为C#支持的任何数据类型。读写方式如下:
将值写入某个键名下:Session("键名")=值;
读取某个键名下的值:变量= Session("键名")
例如:
2)Session对象的属性、方法和事件
常用属性:
Count:获取Session键/值对的个数。
Keys:获取Session所有键/值对中键的集合。
Timeout:获取或设置Session的超时期限(以min为单位),默认为20 min。
Timeout时间值太长则不利于释放内存,太短则可能导致客户端存放在Session中的数据丢失。
常用方法:
Add:向Session中加入一项,语法为;Session.Add(Key,Value);
Remove:按键名从Session中移除一项,语法为:Session.Remove(Key);
RemoveAt:按索引从Session中移除一项,语法为:Session.RemoveAt(Index);
RemoveAll:移除Session中的所有项。调用该方法后,Count属性值将为0。
Clear:移除Session中的所有项,并直接释放服务器Session内存。
常用事件:
Session_Start:新客户端初次请求发生时触发该事件。
Session_End:Session过期时触发该事件。
一般可以在Session_Start事件处理方法做新会话的初始化工作;在Session_End事件处理方法做会话结束时的清理工作。
这两个事件的事件处理方法由Web应用程序直接生成到Global.asax文件中,关于Global.asax文件的内容将在本任务的“拓展与提高”部分进行介绍。
3)实例使用Session保护网页
用一个典型的例子说明Session对象的应用:在网上书店系统中,按后台操作流程,必须在登录页面login.aspx进行登录验证后才能进入后台管理页面main.aspx。但是,如果知道具体的main.aspx页面的URL地址,就能通过在浏览器直接输入该地址直接进入main.aspx页面,从而跳过登录验证这一步骤,这就产生了一个漏洞,使后台管理页面失去用户名和密码的防护。
为了填补这一漏洞,可以使用Session对象,思路如下:在登录页面login.aspx登录成功后,将用户名保存在Session对象中;在后台管理页面加载后,首先验证Session对象中是否包含用户名,如果不存在用户名,就判断为非法进入后台,此后重定向到登录页面login.aspx,要求重新进行登录。否则为合法进入后台,这时就将后台管理页面显示出来。这样就实现了对后台管理页面main.aspx的保护。
①在登录页面login.aspx中,在单击登录按钮生成的事件处理方法中编写代码如下:
②在后台管理页面main.aspx的Page_Load页面加载事件处理函数中检查Session中是否存在用户名,若不存在,就重定向到登录页面login.aspx,要求重新进行登录,代码如下:
③在后台管理页面main.aspx中,一般都应设置一个“退出”或“注销”的功能按钮,在按钮的单击事件处理方法中,将Session[“Login”]清空,然后跳转到前台首页或登录页。尽量不要直接关闭后台页面的浏览器,而应该使用“退出”或“注销”功能按钮退出后台后再关闭。因为如果直接关闭浏览器,由于Session尚未过期,所以Session["Login"]中的用户名仍然存在,非法用户就可能利用这一间隙,通过输入URL直接进入后台页面,“退出”或“注销”的功能按钮的事件处理方法中的代码如下:
【任务实施】
电子商务网站的“购物车”是对商场购物车和购物篮的模拟。在网上书店系统中,用户在各个图书展示页面选购了图书后,网上书店系统会将用户选购的所有图书集中添加到购物车,用户可以在购物车中查看自己所购图书的信息,如书名、数量和价格等,也可以修改图书的数量,或者移除购物车中的图书等。本次任务主要实现将选购图书添加到购物车,查看购物车所购图书信息、移除购物车中的图书3个功能。并在此基础上扩展购物车的其他功能,应该也不会是很困难的事情。
本次任务所采用的实现购物车的基本思路是:利用Session对象能为每个客户端用户独立保存会话信息,并能实现单客户端多页面之间的信息共享的特征,并利用DataTable对象能在服务器内存中保存具有表格结构的数据信息的特点,将图书信息放入DataTable对象,并将DataTable对象放入Session中(Session对象中能接收任何数据类型的对象,包括DataTable),这样,就能实现在Session中规范的保存具有二维表格结构的多条图书信息(每条图书信息包括图书名称,价格、数量等信息项),关于DataTable数据表对象的知识前面已经作过详细介绍。那么,向购物车添加图书信息就是向Session中的DataTable数据表添加记录;查看购物车图书信息就是将Session中的DataTable数据表中的信息显示到页面;移除购物车图书信息就是从Session中的DataTable数据表中删除对应记录。
(1)添加购物车图书信息
在某个图书展示页面,当用户单击了“购买”按钮,此时网上书店系统会将该条图书信息加入到购物车,如图4.7所示。
图4.7加入购物车页面
制作若干个如图4.3所示的模拟图书展示页面,将显示ISBN书号的标签控件的ID设置为lbIsbn,显示书名的标签控件的ID设置为lbBookName,显示实际价格的标签控件的ID设置为lbPrice,为在各页面的购买按钮的事件响应方法中添加对应代码如下:
(2)查看购物车图书信息
查看购物车图书信息,就是在购物车页面上将Session中DataTable中的信息显示出来,如图4.8所示。
图4.8查看购物车页面
创建购物车页面ShopCart.aspx,在该页放置一个GridView控件,ID设置为gvCart,单击该控件右上方的箭头按钮,展开后单击编辑列按钮,在弹出的编辑列对话框中添加4个Bound-Field,分别将HeadText属性设置为“ISBN号”“图书名”“价格”“数量”,将DataField属性设置为“ISBN”“BookName”“MarketPrice”“BookAmount”,就能实现将图书信息按自定义的格式显示出来。在页面的Page_Load事件处理方法中添加如下代码,这部分内容涉及前面项目中介绍过的对网格控件设置数据源、进行数据绑定等内容。
(3)移除购物车图书信息
在购物车页面上,若需实现移除功能,则每条图书信息后面都应有一个删除按钮,以便当用户不想购买某种图书,单击对应的按钮就能移除该种图书信息,如图4.8所示。首先进入网格控件的编辑列对话框,添加一个新的按钮列,将该列的Text属性设置为“移除”,CommandName属性设置为“RemoveBook”。按钮的单击事件首先会被传递给网格控件,并触发网格控件的Row-Command事件,因此,为网格控件RowCommand事件生成事件处理方法,在其中添加如下代码:
需移除行的索引会被自动存放在页面传递的参数e的CommandArgument属性中,得到该索引后,使用它就能从Session的DataTable数据表中删除对应的行,重新绑定数据源后,购物车页面就会重新显示购书信息。
【拓展与提高】
Cookie对象
1)Cookie对象概述
Session对象并不能持久地保存用户会话信息,当用户在限定时间内没有任何操作时,用户的Session对象将被注销和清除,因此,在较长时期保存用户信息时,Session对象并不适用。此时,可以使用Cookie对象持久化地保存客户端用户信息,相比于Session对象保存在服务器端而言,Cookie对象保存在客户端,因此,Cookie对象能够长期保存。
Cookie对象对应的类为HttpCookie类,Request对象和Reponse对象中都包含一个Cookies集合,可以用来访问客户端,读写Cookie信息,Cookie使用方法和Session有所不同。相比于Session而言,Cookie对象有如下特点:Cookie能够在客户端上长期进行数据保存,当然也能自定义过期时间;Cookie内部是一种简单的、文本型的键值对轻量级结构;Cookie存储在本地客户端,无需消耗任何服务器资源;Cookie对象有大小限制;如果客户端设置为禁用Cookie,则Web应用程序无法使客户端保存Cookie;客户端本地保存的Cookie并不安全,能够被恶意程序修改和伪造,这会导致不够健壮的Web应用程序出错。因此,在使用Cookie进行相应的信息或状态的存储时,应充分考虑Cookie的各种特点是否满足需求。
2)Cookie对象常用属性和方法
常用属性如下:
Name:获取或设置Cookie的名称。
Value:获取或设置Cookie的Value。
Expires:获取或设置Cookie的过期日期和事件。
Version:获取或设置Cookie的符合HTTP维护状态的版本。
常用方法如下:
Add:增加Cookie对象。
Clear:清除Cookie集合内的所有对象。
Get:通过键名或索引获得Cookie对象的对应值。
Remove:通过键名或索引删除Cookie对象。
3)创建和访问Cookie对象实例
①创建Cookie对象
下例创建了一个名称为MyCookie的Cookie对象,将当前登录时间保存在该Cookie对象中,并通过Expires属性设置其在客户端的过期时间为5 d,代码如下所示:
HttpCookie MyCookie= new HttpCookie("LoginTime");
MyCookie.Value= DateTime.Now.ToString().;//在Cookie对象中保存当前时间值
MyCookie.Expires= DateTime.Now.AddDays(5);//设置Cookie过期时间为5 d
Response.Cookies.Add(MyCookie);//将Cookie对象通过Response应答对象写入客户端
当向客户端写入Cookie对象后,将会在客户端的Cookies目录下建立一个文本文件,文本文件的Cookie信息以键值对的方式存在。Cookies目录在Windows下是隐藏目录,在该文件夹中可能存在多个Cookie文本文件。
②获取Cookie对象
一旦Cookie对象写入客户端后,且没有被人工清除和过期,就能通过Request请求对象进行读取,下例获取之前创建的Cookie对象中存储的上次登录时间,代码如下:
HttpCookie GetCookie= Request.Cookies["LoginTime"];//通过键名从集合中获取Cookie
Response.Write("上次登录时间:"+ GetCookie.Value.ToString());//输出Cookie对象的值
Web应用程序会经常使用到Cookie,如上例中,用户登录成功时,通过Cookie对用户本次登录时间(也可以是用户的其他信息)进行保存。当用户再次进入登录页时,可以直接获取客户端的Cookie的值进行显示,用于提示用户上次的登录时间。
【教学评价】
评价标准见表4.7。
表4.7任务4.3评价标准
任务4. 4 网上书店在线人数和访问人次的统计
【任务描述】
用户在线人数和网站访问人次是网站系统重要的状态信息,一般会在信息网站的首页显示总的访问人次,以此说明本信息网站的受关注程度;在论坛等交互网站系统的首页显示在线人数,以此说明当前论坛交互系统的热度。在本任务中,将网上书店系统在线人数和访问人次的统计都放在简单的首页上实现和显示。
统计用户在线人数和总访问人次时,由于这两个信息都是所有用户和浏览者共享的,因此不能使用会话级的对象,如Session和Cookie来存储信息,必须有一个能被所有用户和浏览者访问的公共对象,用来保存这两个数据,以便在任何用户和浏览者进入网站首页后,系统都能实时向他们显示在线人数和访问人次。
【知识准备】
(1)Application应用程序对象
1)Application对象概述
Application对象就是一个能被所有用户和浏览者访问的公共对象,用于在Web应用程序中为所有客户端用户存储全局信息,实现所有客户端会话之间的信息共享。可以将任何数据对象作为全局共享信息存入Application对象,Application对象属于HttpApplicationState类。Web服务器会为它的每个Web应用程序运行实例创建一个Application对象,在Web应用程序运行后,当该Web应用程序第一次被客户端访问时创建。
使用Application对象最简单直接的方法是使用键/值对(Key/Value)存储信息,类似于Session对象,例如:
使用Application对象保存信息:Application(“键名”)=值;
获取Application对象信息:变量名= Application(“键名”)。
2)Application对象的常用属性和方法
Count属性:获取Application中对象的个数。
Add方法:向Application中加入一项,格式为:Applicatio.Add(Key,Value);例如:
Remove方法:从Application中移除一项,格式为:Applicatio.Remove(Key);例如:
RemoveAt方法:按索引从Application中移除一项,格式为:Applicatio.RemoveAt(Index);
RemoveAll方法:移除Application中的所有项。
Clear方法:移除Application对象的所有项,并释放服务器内存中的Application对象。
Lock方法:读写时锁定Application以保证同步访问,防止访问冲突。
UnLock方法:读写完成后解除对Application对象的锁定,供其他用户使用。
如果多个用户同时读写一个Application对象的情况,就可能出现读写冲突,造成数据不一致。Lock和Unlock方法专门用于解决Application对象的读写冲突问题,使用方法是:某用户在读写Application对象前锁定它,不允许其他用户读写;当完成读写后再解锁,此时可提供给下一个用户使用,示例代码如下:值得注意的是,使用时,Lock和UnLock应成对出现。
3)Application事件
Application对象有6个典型事件,这些事件都是Web应用程序运行过程中的全局性事件,见表4.8。
表4.8 Application事件
(2)全局事件文件(Global.asax)
Application和Session对象的事件都是Web应用程序的全局性服务器事件,无需手工为事件映射事件处理方法,只需右键点击Web应用程序项目,添加类型为“ASP.NET应用程序”的文件,添加后的文件自动命名为Global.asax,内部包含了8个关于Application和Session对象的全局事件处理方法,只需在方法内部添加功能代码就能实现对事件的响应。Global.asax文件添加后位于Web应用程序项目的根目录下,内容如下:
各个全局事件处理方法的执行时机描述如下:
①Application_Start事件处理方法
当Web应用程序启动时执行,该方法在Web应用程序的运行过程中仅仅被触发一次,因此,通常将Application对象的初始化代码放在这里。
②Application_End事件处理方法
当Web应用程序关闭时执行,该方法在Web应用程序的运行过程中同样仅仅被触发一次。当Web应用程序关闭时,需释放应用程序占有的资源,此时可以将释放资源的代码放在这里。
③Session_Start事件处理方法
当Web应用程序被一个新客户端初次请求资源时执行,在Web应用程序的运行过程中,不断会有新的客户端第一次对其进行请求,因此,Session_Start方法会被反复执行,可以将为每个客户端用户单独存储信息的Session对象的初始化代码放在这里。
④Session_End事件处理方法
当Session对象失效时执行,当Web服务器一段时间后没有检查客户端用户的提交操作会自动关闭与该客户端的连接,并销毁对应的Session对象,使Session对象失效,这段时间就称为Session过期时限,可以人工设置Session过期时限的值,在这一时限内即使没有任何提交请求Session对象仍然维持有效。
⑤Application_BeginRequest事件处理方法
当客户端向Web应用程序发出请求后执行。在Web应用程序的运行过程中,不断会有客户端频繁地对其进行请求,因此,Application_BeginRequest方法会被反复执行。
⑥Application_EndRequest事件处理方法
当对客户端的请求处理完成后执行。在Web应用程序的运行过程中,不断会有客户端的请求被处理完成,因此,Application_EndRequest方法也会被反复执行。
⑦Application_Error事件处理方法
当Web应用程序发生异常时执行,可以将异常处理代码写在该方法中。
⑧Application_AuthenticateRequest事件
当发生身份验证请求时执行。可以将自定义身份验证代码写在该方法中。
【任务实施】
(1)统计并显示网站访问人次
①添加Global.asax文件,打开该文件,在Application_Start事件处理方法中,将网站访问人次初始设置为零,代码如下:
②在网站首页Default.aspx,如果存在一次对首页的请求(非回传),则认为网站访问人次增加一次。因此,在首页的Page_Load方法中实现网站访问人次加1,并显示出该数据。
(2)统计并显示网站在线人数
①打开Global.asax文件,在Application_Start事件处理方法中,将网站在线人数初始设置为零,代码如下:
②在Global. asax文件中,在Session_Start事件处理方法中将网站在线人数加1,在Session_End事件处理方法中将网站在线人数减1,代码如下:
③在网站首页的Page_Load事件处理方法中添加代码,将网站在线人数显示在Label标签控件中,代码如下:
【拓展与提高】
ViewState
1)ViewState概述
ViewState是ASP.NET用于保持在提交回传后页面状态的一种机制。当页面被提交给服务器,服务器处理后再进行页面回传时,表单中控件的状态数据已被清空。也就是说,提交前的页面和回传后的页面除外观相似外,实质上没有任何联系。ViewState实质是页面自动生成的一个隐藏域的属性,当页面提交时,ASP.NET会把页面和控件的状态信息,序列化为一个字符串,作为ViewState隐藏属性的值;当页面回传时,ASP.NET会根据ViewState隐藏属性恢复页面和控件在提交前的状态。这样,就能使Web页面在多次提交回传前后保持状态。
如果Web页面的表单标明“runat= server”,则ASP.NET会自动给页面添加一个隐藏域,形式大致如下:
该隐藏域控件的value值并非加密信息,而是页面和控件的状态信息经过序列化编码后形成的。
2)ViewState的使用
ASP.NET中的ViewState是一种保持页面状态的自动机制。同时,ASP.NET也为用户使用ViewState保存自己的页面级的状态信息提供了接口。Control类(Web控件基类)中提供了一个ViewState属性,其他Web控件由于都是Control类的子类,因此自动获得了ViewState的功能。可以通过使用类似于Session和Application的“键值对”读写方式使ViewState保存自定义的页面状态信息。
例如:
3)ViewState其他说明
①ViewState的有效期等效于页面的生存期,作用域局限于当前页面,因此它的生存期比Application、Cookie和Session短,作用域也比这三者小。
②ViewState仅仅支持String、Integer、Boolean、Array、ArrayList、Hashtable以及其他一些自定义的较简单的数据类型,这些数据类型的共同特点是都支持序列化。
③Page类和Web控件类都有一个EnableViewState属性,为ViewState功能的开启或关闭提供了控制接口。
④由于ViewState信息存储在Web页面,当页面提交回传时,ViewState信息也会被提交回传,因此如果存储了较大的值,Web页的传输速度会明显减慢。
⑤由于ViewState未被加密,且保存在页面上,所以安全性较差,利用反序列化工具就能轻易地将信息解析出来,一种解决方法就是对ViewState信息进行手工编程加密。因此,在使用ViewState时必须对安全性有所警惕。
【教学评价】
评价标准见表4.9。
表4.9任务4.4评价标准
【归纳总结】
①本项目主要介绍Web应用系统内部的一些重要机制,主要包括两个方面:一是Web应用系统的客户端(浏览器)和服务器端如何进行信息交互;二是Web应用系统如何保存状态信息。这些机制被封装成了一些内部对象和Ajax控件,通过使用它们,就能实现交互操作和状态保持。相对于内部对象,Ajax控件的封装程度更高,已经完全脱离了其最初的形态。要想了解Ajax的最初实现,请读者通过网络或其他方式进行自学。
②Web应用系统的典型交互方式是一种提交——回传模式,即客户端提交请求,服务器端处理请求,然后回传处理结果,Request对象和Response对象分别实现对提交请求过程和回传应答过程的封装。每次提交回传都会导致整个页面的重新生成,从用户角度看,整个页面发生了一次刷新;AJAX技术在浏览器和服务器之间设置一个中间层,提供了一个Web Service类型的服务接口,专门接收用户请求并进行响应。这种方式不会产生典型的提交回传,用户感觉不到页面的刷新,从而降低了网络资源的耗费,提升了用户体验。ASP.NET的Ajax控件正是对Ajax类型交互过程的封装。
③典型的提交回传会导致页面重新生成,在这种机制下,页面是无法保持前后状态的一致的。因此,Web应用系统提供了一些附加机制来保持状态:可以使用Session和Cookie对象将会话级状态信息保存服务器端和客户端;可以使用Application在服务器端保存应用程序级的状态信息;可以使用ViewState保存页面级的状态信息,使页面在提交回传后仍能保持状态。
【练习与实训】
实训1模拟实现商品搜索页面,用户输入商品名称、生产商,将这两个关键词分别通过URL地址参数、Session读写和Cookie读写的方式传递到另一页面(搜索结果页面)上显示出来。
实训2模拟实现一个简单的聊天室,该聊天室只需提供用户发言的功能即可,且无需将聊天信息长久地保存在数据库中。(提示,使用Application保存所有用户的聊天信息,当某用户在文本框中输入聊天信息后,单击发言按钮时,将Application中的聊天信息重新显示在页面上,每条聊天信息在页面上占一行)。