第四章.高级主题 显示状态的维护 组件的显示状态是指组件在一系列的postback中应该始终保存的数据。比如,用户在一个页面中更改了一个组件的字体颜色,他希望页面再一次提交显示的时候,依然看到的是已经修改过的颜色。在桌面GUI程序的开发中,这个非常简单。但是在Web编程中就不那么容易了,因为Web服务器并不会一直在内存中保持着页面状态。PRADO借鉴了ASP.NET的显示状态这个概念来解决这个问题。
如果要通过显示状态来保持一些数据,可以调用TComponent 类中的getViewState() 和 setViewState() 方法。可以在显示状态中保持很多类型的数据,但是不用把它用在资源句柄中,比如数据库联接。
PRADO 通过一个把限制状态保存在一个隐藏字段中来返回给用户。当用户提交页面并再一次显示页面的时候,显示状态中的数据会从被提交的隐藏域中读出来并用此恢复页 面的状态。
可以通过两种方式来定制显示状态的存储方法。第一种通过重载TPage的 loadPageStateFromPersistenceMedium()和savePageStateToPersistenceMedium()方法实现,比如可以把显示状态保存在session中或者数据库中,避免通过隐藏字段要传输大量的数 据。第二种是使用应用配置中的显示状态处理器。
Session Session是用来在不同页面间保存状态的,这和页面的显示状态机制不同,显示状态只能保存本页面的状态。
如果要使用session, 那么首先在应用的配置文件中定义一个session类。session类必须实现ISession 接口。如果你不想自己写一个session类,那可以使用类System.Security.TSession ,它封装了$_SESSION 变量来实现了ISession 接口。你也可以自己写一个session类来使用不同的方法处理session(比如把session保存到数据库中)。
如果定义了session类,在请求的页面创建之前,对应的session对象就会被创建。TPage 页面类有一个Session 属性,可以方便的来访问session对象。
验证和授权 PRADO 提供了一个验证/授权的框架来支持验证页面的访问者,判断他们是否有权限来访问页面。
在验证框架中,一个用户类起到了主要作用。访问者如果通过了一定的身份验证(比如登录),并在服务器端获得了一个正确的身份,那么验证就通过了。用户对象代表了这么一个身份识别。除了身份检查外,一些页面也需要额外的权限检查来确保被验证的身份有足够的权限来访问。
验证框架支持基于角色的验证。一个页面如果在应用的配置页面中指名了需要验证(secured),框架将确保在访问页面前用户必须被验证。如果还指定的角色,那么框架还将确保用户是属于该角色的。最后,还可以定制页面的验证过程。如果验证或者授权失败了, onAuthenticationRequired 或onAuthorizationRequired 方法就会被触发,可以在此显示错误信息或者重定向到登录页面。
用户对象在不同的页面始终存在的,它使用了session。因此如果要使用验证框架则必须申明session类。由于用户对象保存在session中,因此你可以用它来保存一些需要在不同页面访问的数据。
如果要使用验证框架,需要在应用的配置文件中申明一个用户类。用户类必须实现了IUser接口。框架中已经提供了一个实现了该接口的类System.Security.TUser 。
phonebook 这个例子中说明了验证框架是如何使用的。
表单验证 PRADO提供了一种简单的方法来进行form的验证,而如果使用传统的PHP编程方式,这个过程是非常重复而且单调的。在PRADO中有一套验证组件。这些验证组件即允许在客户端也允许在服务器端对数据进行验证,下面我们将举一个例子来说明。
现在我们要创建一个用户注册页面。这个页面要求用户输入一个用户名和一个密码来创建新的帐号。密码要求是被输入两次以防止输入错误。另外用户名和密码都只能包含数字和数字,而且两者的长度分别要求不少于3位和6位。
要实现这个功能,可以创建这么一个模板:
...
<com:TForm>
...
Username:
<com:TTextBox ID="username" />
<com:TRequiredFieldValidator
ControlToValidate="username"
ErrorMessage="You must choose a username." />
<com:TRegularExpressionValidator
ControlToValidate="username"
RegularExpression="[\w]{3,}"
ErrorMessage="Username must ...." />
<br/>
Password:
<com:TTextBox ID="password" TextMode="Password" />
<com:TRequiredFieldValidator
ControlToValidate="password"
ErrorMessage="You must choose a password." />
<com:TRegularExpressionValidator
ControlToValidate="password"
RegularExpression="[\w]{6,}"
ErrorMessage="Password must ...." />
<br/>
Repeat Password:
<com:TTextBox ID="password2" TextMode="Password" />
<com:TRequiredFieldValidator
ControlToValidate="password2"
ErrorMessage="Please re-type your password." />
<com:TCompareValidator
ControlToValidate="password2"
ControlToCompare="password"
ErrorMessage="Your password entries did not match." />
<br/>
<com:TButton Text="Register" />
...
</com:TForm>
...
上面这个模板允许页面执行用户输入的客户端验证和服务器端验证(假定浏览器支持javascript)。不再需要额外的代码了。如果验证失败的话,form是不会被提交的,而同时会显示错误信息。
可以在模板文件中通过设置验证组件的EnableClientScript 属性来关掉客户端的验证。这时候,你可以在页面的OnLoad 事件中或者之后通过检查页面的IsValid 属性来判断验证是否通过。
缓存 PRADO框架提供了一个缓存机制来提高PRADO应用的运行效率。
该缓存的配置主要取决于应用配置文件中定义的缓存处理器。系统默认使用TCacheManager类,我们将在这里详细描述它是如何管理缓存的。
对于每一种组件类型,如果它之前曾经被实例化过,那么就会有一个对应的文件存在缓存目录中。这个文件包含了被序列化的的控件实例。以后一旦需要再创建同样的组件,那么就直接通过这个缓存文件来创建。这将会大大节省组件的创建时间,因为创建组件的过程需要解析好几个XML规格文件和模板文件。
如果要使用缓存,只需要在应用的配置文件中给的属性设置一个目录即可。这个目录必须是Web服务器可写的。使用的路径可以是绝对路径也可以是相对路径。
注意,如果你修改了组件的文件,那么应该包括所有的缓存文件都删掉或者移走。下一次组件创建的时候会自动重新创建的。因此在开发的时候,应该把缓存关掉。
定制TApplication TApplication 类是可以被扩充的。 TApplication 有好几个方法可以被重载。比如,你可以重载 beginRequest() 方法来实现对request数据的进行一些自己需要的预处理。如果想进一步了解这些内容,可以参考PRADO 的文档。
显示状态安全性 可以通过几种方式保护显示状态被恶意用户通过结果查看和修改。PRADO应用必须采用一到多种方式来保护显示状态的安全。这些方式都和应用配置文件的显示状态的处理器的设置相关。
显示状态处理器能够通过以下语法进行配置,
<vsmanager class="TViewStateManager"
enabled="false"
buffer-size="10"
key="secret key"
encrypt="false" />
所有的属性都是可选的,当忽略时,使用默认设置。
Class属性指定显示状态处理器的处理类,默认情况下,使用 TViewStateManager Enabled属性指定是否采用session存储显示状态的数据。默认情况下,该值为false.采用session进行显示状态的存储,由于是使用服务器端存储数据,因此是安全的。然而,对每一个session对象进行存储,需要保持大量的状态信息,加大了服务器端负荷。 Buffer-size属性指定在设置使用session的enabled属性被设置为true的情况下,将保存多少个页面显示状态信息。默认情况下,该值为10,也即是用户可以通过浏览器的后退10次之内,不丢失任何输入的信息。增加该值,将增大服务器端储存的数据量。 Key属性指定一个密匙用于为显示状态产生一个MAC并测试显示状态是否修改。Key并不是必须的。如果允许session,PRADO将产生随机产生一个Key。当然,如果禁用session,你必须设置一个Key值,用于保护你的显示状态数据。 Encrypt属性指定是否进行加密,防止客户端用户查看显示状态。默认情况下,该值为false 总的来说,有三种方式保护显示状态数据的安全,
在应用配置文件中设置vsmanager的enabled属性为true,把显示状态存储在 session中。这种方式是最安全的,但会增大服务器负荷。 当通过客户端表单的隐藏字段进行显示状态数据存储时,使用加密存储。你可以设置encrypt属性为true并在session禁用时产生一个密匙。注意,如果加密算法效率低下,将减低应用程序的性能。在TViewStateManager中,取决于密匙长度采用DES或triple DES算法。密匙长度最少8个字节。 允许使用MAC进行显示状态完整性检测,这一步骤是必须支持的。然而,在session禁止时,你需要提供一个密匙。当显示状态通过客户端表单隐藏字段存储时,进行完整性检测可以保护显示状态数据。 使用母版页 在2.0版本后开始引入母版页特性。该特性主要解决几个页面需要共享一种布局的问题,如左右边栏,搜索输入框,菜单等。通过调用外部模板文件可以部分解决此问题。母版页提供了一个更加系统更出色的的解决方案。
假设我们想要创建几个页面共享一个菜单拦或左右侧栏。注意,菜单中的几个菜单项需要事件处理,如THyperLink的OnClick动作。我们可以使用母版页显示公共部分。母版页和PRADO正常模版页面一致,除了模版页面中还包含TContentPlaceHolder控件外。每一个 TContentPlaceHolder保留了一个可以允许内容页面插入显示结果的区域,范例如下:
<com:TForm>
<com:THyperLink Text="Login" OnClick="loginUser" />
<com:TContentPlaceHolder ID="content" />
</com:TForm>
</body></html>
一个内容页面通过在该内容页面模版中使用以下语句关联母版页,
<%@Page Master="master page name" %>
内容页面在其模板中包含了一到多个TControl控件。每一个TControl都拥有一个ID,该ID与母版页中的 TContentPlaceHolder ID 相一致。在相一致的情况下,TContent控件的内容将插入到TContentPlaceHolder定位的位置。例如:
<%@Page Master="master page name" %>
<com:TContent ID="content">
<com:TDataGrid ID="grid" />
<com:TButton Text="Post some data" OnClick="Page.onClickSubmitBtn" />
</com:TContent>
母版页的业务逻辑和表现内容是互相分离。通常情况下,每一个页面内控件的事件响应函数定义在相应的模板中。
母版页可以嵌套使用,例如,一个母版页可以是另一个的母版页。
在代码中,可以通过以下的方式访问母版页或内容页面的组件:
// This will set the <com:TImage> ImageUrl that resides in the MasterPage
$this->MasterPage->HomeImg->setImageUrl('images/button1-selected.gif');
// This will set the <com:TImage> ImageUrl that resides in the Content Page
$this->content->HomeImg->setImageUrl('images/button1-selected.gif');