车必须装有侧反射器:ASP.NET应用程序规划与设计

来源:百度文库 编辑:九乡新闻网 时间:2024/05/06 11:48:53

ASP.NET应用程序规划与设计

文档化用户方案

用户方案没有什么令人惊异之处。通常,它们只是描述用户如何与应用程序交互。用户方案的关键价值在于记录了关于每个人对用户希望系统如何运行以及应用程序应如何响应的设想。通过完成这个过程,您将可以完全了解处理各种用户与系统的交互时所需的数据点和函数。换句话说,编写完善的用户方案将有助于您确定完成解决方案需要实现的数据库、中间件和用户界面元素。

注意:Visual Studio .NET Enterprise Architect 有一项非常不错的功能,即允许您使用 Microsoft Visio? 通过 UML(统一建模语言)创建用户方案,然后生成这些方案的基本代码。在这里,我不打算深入探讨这些细节,但是您可以在 MSDN? Academic Alliance 站点找到一篇关于这一主题的好文章 Generating .NET Code Using Visio Enterprise Architect‘s UML,作者是 Sreedhar Koganti。

有了上一节的目标声明后,下面是 DotNetKB 项目的几个示例用户方案。

搜索知识库

匿名用户可以输入一个或多个关键字并执行搜索,搜索将返回包含这些关键字的问题和/或回答列表。用户可以将关键字搜索锁定在仅搜索问题、仅搜索回答或者二者都搜索。返回的列表将显示问题及其回复数和被其他用户访问的次数。单击链接将返回以时间先后逆序排列的回复(纯文本)列表。

将新问题输入到知识库中

匿名用户可以浏览用于向数据库输入新问题以供授权专家审阅和回复的屏幕。用户可以输入问题的标题和内容,并可以选择在一系列主题中的某个主题下记录该问题。用户还可以输入他们的名字和相关的 URL(电子邮件、Web 地址等)。输入将被验证,以确保包含必需的数据并确保所有输入数据不会受到脚本攻击等。一旦数据经过验证并被保存到数据库中,用户将看到一个响应屏幕,感谢用户的支持并将用户直接连接到主页。此外,用户还可以选择让该站点“记住”他们的姓名和 URL 以备以后访问该站点时使用。

您已经了解它的工作原理了,对吗?每一个方案都尝试细化用户交互的重要方面。例如,上面列出的两个方案表明用户为“anonymous”(匿名用户),这表示这类用户不需要登录或进行其他方式的授权。第二个示例还标识了若干输入值、验证步骤和可选操作。

当然,这只是两个示例;完整的系统需要更多的方案。此外,需要特别注意的是,“用户”不仅仅可以是人,也可以是您的程序需要与其通信的其他应用程序,甚至还可以是您的应用程序的其他部分。例如,一个方案描述主页如何列出最近添加到知识库中的内容,以供任何人查看。此例中的“用户”将是主页自身。还有一些方案描述专家如何查找和回复新问题以及管理员如何更新主题列表并管理系统的其他部分。我已为讨论这个简单的应用程序标识了 20 多种方案。您可以在 DotNetKB 中找到当前列表(以及与此项目相关的所有其他资料)。

至此我们就有了目标声明和一些用户方案。现在,是时候稍憩一下,然后学学一些技术了。我们需要定义应用程序体系结构,这可以帮助我们以“鲜活有效的代码”实际实现方案。

定义应用程序体系结构

有了基本的目的和为解决方案开发的用户方案列表后,您需要开始筹划整体的体系结构。主要目标是标识应用程序的逻辑方面和物理方面,即如何将应用程序拆分为各种有用的部分。在本节中还添加了安全性方面的内容。安全是在规划的“一开始”您就需要考虑的问题,而不是在开发周期中“最后添加”的内容。我们稍后会在本节中详细讨论这个问题。

逻辑体系结构

从逻辑上讲,您需要规划解决方案以标识数据存储、数据访问、业务规则、用户界面等之间的“边界”。通常,Web 开发人员会选择一个两阶段模型,并用 Web 窗体存储用于访问现有数据存储系统(例如 Microsoft SQL Server)的所有代码。一个更有效的方法是创建一个位于 Web 窗体用户界面与 SQL Server 数据存储系统之间的中间层组件库。这种三层方法(Web 窗体、组件、数据库)通常是大多数应用程序所需的。但是,在某些情况下,可能需要一个其他层来处理服务器之间传输的数据。这个传输层可以使用独立于平台的协议(例如 XML-SOAP)来实现。但是,如果您从头到尾都使用 Microsoft .NET 技术,则可以使用 .NET 远程协议的二进制版来完成这一任务,而且速度比使用 XML-SOAP 要快得多。

对于我们的示例,我们将定义三个逻辑边界:用户界面(Web 窗体)、中间层(一个 .NET 组件程序集)和数据层(SQL Server 数据库)。图 1 显示了如何表示这一内容。

图 1:三层图

现在我们有一个简单的逻辑模型。它是如何起作用的?它有助于我们考虑各个逻辑组之间的边界。每个逻辑层应尽量与其他层独立。理想的情况是,图层中的更改应该对整体产生最小的影响。例如,如果将数据存储从 SQL Server 更改到 XML 数据文件,唯一受到影响的图层应是中间层图层。用户界面应该根本无需考虑更改。这会使您进行思考:如何实现解决方案的实际编码以实现此原则。

另外,逻辑层有助于我们考虑安全问题。各个图层之间的边界都存在潜在的安全漏洞。而且,各个图层可能有自己特定的安全措施(SQL Server 权限、.NET 运行时权限、ASP.NET 安全等)。同样,我们稍后会在本节中详细讨论这个问题。

文档化用户方案

用户方案没有什么令人惊异之处。通常,它们只是描述用户如何与应用程序交互。用户方案的关键价值在于记录了关于每个人对用户希望系统如何运行以及应用程序应如何响应的设想。通过完成这个过程,您将可以完全了解处理各种用户与系统的交互时所需的数据点和函数。换句话说,编写完善的用户方案将有助于您确定完成解决方案需要实现的数据库、中间件和用户界面元素。

注意:Visual Studio .NET Enterprise Architect 有一项非常不错的功能,即允许您使用 Microsoft Visio? 通过 UML(统一建模语言)创建用户方案,然后生成这些方案的基本代码。在这里,我不打算深入探讨这些细节,但是您可以在 MSDN? Academic Alliance 站点找到一篇关于这一主题的好文章 Generating .NET Code Using Visio Enterprise Architect‘s UML,作者是 Sreedhar Koganti。

有了上一节的目标声明后,下面是 DotNetKB 项目的几个示例用户方案。

搜索知识库

匿名用户可以输入一个或多个关键字并执行搜索,搜索将返回包含这些关键字的问题和/或回答列表。用户可以将关键字搜索锁定在仅搜索问题、仅搜索回答或者二者都搜索。返回的列表将显示问题及其回复数和被其他用户访问的次数。单击链接将返回以时间先后逆序排列的回复(纯文本)列表。

将新问题输入到知识库中

匿名用户可以浏览用于向数据库输入新问题以供授权专家审阅和回复的屏幕。用户可以输入问题的标题和内容,并可以选择在一系列主题中的某个主题下记录该问题。用户还可以输入他们的名字和相关的 URL(电子邮件、Web 地址等)。输入将被验证,以确保包含必需的数据并确保所有输入数据不会受到脚本攻击等。一旦数据经过验证并被保存到数据库中,用户将看到一个响应屏幕,感谢用户的支持并将用户直接连接到主页。此外,用户还可以选择让该站点“记住”他们的姓名和 URL 以备以后访问该站点时使用。

您已经了解它的工作原理了,对吗?每一个方案都尝试细化用户交互的重要方面。例如,上面列出的两个方案表明用户为“anonymous”(匿名用户),这表示这类用户不需要登录或进行其他方式的授权。第二个示例还标识了若干输入值、验证步骤和可选操作。

当然,这只是两个示例;完整的系统需要更多的方案。此外,需要特别注意的是,“用户”不仅仅可以是人,也可以是您的程序需要与其通信的其他应用程序,甚至还可以是您的应用程序的其他部分。例如,一个方案描述主页如何列出最近添加到知识库中的内容,以供任何人查看。此例中的“用户”将是主页自身。还有一些方案描述专家如何查找和回复新问题以及管理员如何更新主题列表并管理系统的其他部分。我已为讨论这个简单的应用程序标识了 20 多种方案。您可以在 DotNetKB 中找到当前列表(以及与此项目相关的所有其他资料)。

至此我们就有了目标声明和一些用户方案。现在,是时候稍憩一下,然后学学一些技术了。我们需要定义应用程序体系结构,这可以帮助我们以“鲜活有效的代码”实际实现方案。

定义应用程序体系结构

有了基本的目的和为解决方案开发的用户方案列表后,您需要开始筹划整体的体系结构。主要目标是标识应用程序的逻辑方面和物理方面,即如何将应用程序拆分为各种有用的部分。在本节中还添加了安全性方面的内容。安全是在规划的“一开始”您就需要考虑的问题,而不是在开发周期中“最后添加”的内容。我们稍后会在本节中详细讨论这个问题。

逻辑体系结构

从逻辑上讲,您需要规划解决方案以标识数据存储、数据访问、业务规则、用户界面等之间的“边界”。通常,Web 开发人员会选择一个两阶段模型,并用 Web 窗体存储用于访问现有数据存储系统(例如 Microsoft SQL Server)的所有代码。一个更有效的方法是创建一个位于 Web 窗体用户界面与 SQL Server 数据存储系统之间的中间层组件库。这种三层方法(Web 窗体、组件、数据库)通常是大多数应用程序所需的。但是,在某些情况下,可能需要一个其他层来处理服务器之间传输的数据。这个传输层可以使用独立于平台的协议(例如 XML-SOAP)来实现。但是,如果您从头到尾都使用 Microsoft .NET 技术,则可以使用 .NET 远程协议的二进制版来完成这一任务,而且速度比使用 XML-SOAP 要快得多。

对于我们的示例,我们将定义三个逻辑边界:用户界面(Web 窗体)、中间层(一个 .NET 组件程序集)和数据层(SQL Server 数据库)。图 1 显示了如何表示这一内容。

图 1:三层图

现在我们有一个简单的逻辑模型。它是如何起作用的?它有助于我们考虑各个逻辑组之间的边界。每个逻辑层应尽量与其他层独立。理想的情况是,图层中的更改应该对整体产生最小的影响。例如,如果将数据存储从 SQL Server 更改到 XML 数据文件,唯一受到影响的图层应是中间层图层。用户界面应该根本无需考虑更改。这会使您进行思考:如何实现解决方案的实际编码以实现此原则。

另外,逻辑层有助于我们考虑安全问题。各个图层之间的边界都存在潜在的安全漏洞。而且,各个图层可能有自己特定的安全措施(SQL Server 权限、.NET 运行时权限、ASP.NET 安全等)。同样,我们稍后会在本节中详细讨论这个问题。

物理体系结构

确定逻辑层后,考虑物理层也很重要。例如,您可以在同时安装有 SQL Server、Internet Information Server、ASP.NET 和 .NET 运行时的单个实际计算机上实现这个应用程序。这将是一个物理层。但更可靠且可扩展的方法是:在由三个 Web 服务器组成的簇上部署 Web 窗体,在两个应用服务器上部署 .NET 组件程序集,在两个故障恢复模式的 SQL Server 上部署数据库。这样产生的物理体系结构将七个 Windows 服务器包含在三个主要组中:Web 簇、组件簇和数据库簇。如果您了解系统的不同逻辑部件可以位于不同的计算机上,您可能会实现不同的代码。

对于我们的示例,我们采用一个有效且强大的两层模型:Web 服务器托管用户界面和组件,数据库服务器托管 SQL Server 数据存储。如果通信量非常大,这个模型使我们可以灵活地在簇中添加更多的服务器,并使其保持足够的简洁以便于处理。下面的图像显示了此物理体系结构与前面定义的逻辑体系结构之间的映射关系。

图 2:物理体系结构与三层体系结构之间的映射关系

正如您看到的那样,逻辑体系结构和物理体系结构不必相同。在规划阶段还要考虑一项内容:安全。

安全规划

Microsoft 有一个关于安全性与软件这一主题的歌诀:“Secure by design, secure by default, and secure by deployment(设计安全,默认安全和部署安全)”。即,在安全中设计,期待系统在默认情况下是安全的,以及创建可以在安全环境中成功部署的解决方案。安全始终是重要的。既然越来越多的软件要在公用的 Internet 上“生存”,编写安全的软件就更加关键。对于我们而言,幸运的是,.NET 运行时和 Windows 操作系统提供广泛的安全选项和功能,我们可以轻松地将其包含在我们的应用程序中。无需过分注重标识和消除联机解决方案中安全漏洞的细节,我们可以指出其中一些最常见的漏洞并指出我们的应用程序规划如何进行处理。

注意:有关可用选项的详细信息,请参阅 Microsoft Security Developer Center。

缓冲区溢出

这可能是已编译应用程序中最常见的安全漏洞。由于我们将使用 .NET 运行时,而它是设计用来在内存中安全运行的,因此不太可能发生缓冲区溢出。此外,我们使用 Microsoft Visual Basic? .NET 对解决方案进行编码,而 Microsoft Visual Basic? .NET 不像 C 或 C++ 那样容易受到缓冲区溢出问题的影响。但是,即使我们打算用 C++ 创建组件,我们还可以使用编译程序的特殊功能,GS 转换,来保护我们免受大多数缓冲区溢出的攻击。

数据库攻击

另一种常见的安全漏洞可能会使恶意用户获得访问存储在数据库中的原始数据的权限。为了防止黑客获得数据的控制权,我们仅使用 SQL Server 存储过程,而不使用“内联查询”。这样可以大大减少试图在输入流中插入其他 SQL 命令的攻击。我们还在程序中多个位置处使用输入验证,以确保所有输入仅包含有效的字符。

交叉站点脚本攻击

对 Web 应用程序进行的常见攻击还有一种,它涉及到用户在输入流中添加客户方脚本,这类攻击将执行附加的对话并诱骗用户将个人数据发送到黑客自己的 Web 站点。要解决这个问题,我们使用 ASP.NET 1.1 的一个新功能,过滤出这种恶意代码的所有输入,防止将它置入系统中。显示屏幕上还包含附加代码,它将自动禁用任何脚本或显示可能会插入到数据存储中的标记。

至此,我们已获得了应用程序的逻辑模型和物理模型,以及确保实现方案包含的安全功能清单。拥有了这些以及目标声明和用户方案,我们可以开始这次“编码前”探险的最后一部分了。

完成设计文档

在直接进入项目的编码部分之前,需要花一点时间实际勾画出应用程序的逻辑组件,这非常重要。在我们的示例解决方案中,我们要实现解决方案的三个逻辑组件:数据库、.NET 数据访问组件和 ASP.NET 用户界面。在下面几篇文章中,我们将非常详细地介绍如何实现这些组件。但现在,我们只是勾画出每个组件的大致轮廓,讨论过程中最重要的方面,即文档化组件间的交互。

数据库

对于 DotNetKB 应用程序,我们需要将数据存储在三张表中:主题、问题和回答(请参阅下图)。

图 3:主题、问题和回答表

我们需要使用存储过程,以使中间层组件也可以安全地访问数据。有关数据库的细节,我们将在下一篇文章中讨论。这里,我们只是指出:列出表名称及所有列细节、默认索引和存储过程列表的数据库文档,应该包含在一个完整的数据库设计文档中。即,文档中应该具有成功实现系统数据存储部分所需的详细信息。

注意:如果留心的话,您可能会注意到我们未提及将专家数据存储在数据库中。只是为了使项目更加有趣(同时给我们一个机会使用直接 XML 数据存储),我们将专家信息存储在一个 XML 数据文件中。

数据访问组件

数据访问组件设计文档描绘与数据存储系统的交互以及与用户界面的交互的所有细节。在有些系统中,数据访问组件实际上是处理过程中各种问题的多个程序集。例如,可能会有一系列业务规则呈现在与数据存储和检索完全独立的用户界面上。在这种情况下,将业务组件与数据访问组件分开实现可能比较明智。

在我们的示例中,实际实现的是两个单独的组件:Message 组件和 DataAccess 组件。如果在支持基于 XML 的数据的传输服务(例如 SOAP Web Service)中进行规划,这种面向消息的实现方案将会特别有成效。

消息组件

消息组件定义一系列用于在各图层之间传输数据的类。这些消息可以作为二进制或 XML 文本数据存在。消息图层的价值在于:保护系统的其余部分,使其独立于数据存储实现方案的具体细节,例如 SQL Server、XML 文件等。此外,通过实现消息图层而不是更复杂的“智能对象”库,我们的解决方案可以更轻松地支持那些不能同时发送数据和类级别逻辑的远程调用服务,例如 XML-SOAP。

下面是一个消息类示例,在该示例中实现了 Topic 消息及其集合:

Public Class Topic

Private _ID As Integer

Private _Title As String

Private _Description As String

Public Property ID() As Integer

Get

Return _ID

End Get

Set(ByVal Value As Integer)

_ID = Value

End Set

End Property

Public Property Title() As String

Get

Return _Title

End Get

Set(ByVal Value As String)

_Title = Value

End Set

End Property

Public Property Description() As String

Get

Return _Description

End Get

Set(ByVal Value As String)

_Description = Value

End Set

End Property

End Class

Public Class Topics

Inherits System.Collections.CollectionBase

Default Public Property Item(ByVal index As Integer) As Topic

Get

Return CType(List(index), Topic)

End Get

Set(ByVal Value As Topic)

List(index) = Value

End Set

End Property

Public Function Add(ByVal s As Topic) As Integer

Return List.Add(s)

End Function

Public Sub Remove(ByVal index As Integer)

List.Remove(index)

End Sub

End Class

注意:如果您已尝试过面向消息的设计,便会了解我们想要使这些消息类系列化,以便在应用程序图层之间轻松地来回发送。幸运的是,.NET 运行时知道如何进行这项操作,而无需我们做过多的工作。但是,当我们学习创建消息的文章时,我们将详细讨论 .NET 运行时如何系列化类,以及我们如何进行操作以使代码中的过程最优化。

在后面实现消息组件和数据访问组件时,文章中将介绍此方法的细节。设计文档将包含一个由所有信息及其属性与数据类型组成的列表。现在,我们只是考虑如何使用此消息方法来封装图层间的数据传输,如何创建一种与本地方案和远程方案配合使用的常规数据服务。

数据访问组件

定义消息类的概念后,数据访问组件可以集中精力处理与数据存储系统直接对话的细节,并以正确的消息格式返回信息。在我们的示例中,这将涉及到使用来自用户界面的请求映射 SQL Server 存储过程,并创建可返回到用户界面进行显示的消息(或消息集合)。

例如,下面是一个数据访问组件的一部分示例代码,该组件将从数据存储中检索单个 Topic 记录,并将正确的消息格式返回到用户界面。

Public Function GetTopicRecord(ByVal ID As Integer) As Messages.Topic

Dim t As Messages.Topic = New Messages.Topic

cn = New SqlConnection(secureConnectionString)

cd = New SqlCommand("GetTopic", cn)

cd.CommandType = CommandType.StoredProcedure

cd.Parameters.Add("@ID", ID)

cn.Open()

dr = cd.ExecuteReader()

dr.Read()

With t

.ID = ID

.Title = dr("Title")

.Description = dr("Description")

End With

Return t

End Function

设计文档将包括一系列用于处理来自用户界面的各个请求的类和方法,并含有有关调用哪个存储过程以及返回何种消息格式的详细信息。同样,我们将在后面主要介绍数据访问图层的文章中讨论此过程的细节。

Web 用户界面

最后,用户界面设计文档将包括完成各种方案所需的所有用户输入和显示。通常来说,用户界面文档包括界面机制的细节以及使用户界面呈现唯一性的图形设计元素。例如,配色方案、字体和总体页面设计,与用于获取搜索查询的正确数据的输入名称和输入数量一样重要。

要使文档简洁,通常在一个与图形设计单独的文档中概要描述机制细节。这是我们将要在示例中做的工作。在后面的一篇文章中,我们将创建一个综合性用户界面文档和实现方案,详细说明每个屏幕的元素和相关操作。在另一篇文章中,我们将处理应用程序有关图形的各个方面,重点讨论作为一种外观服务的级联样式表的使用。

下面是一个典型的用户界面描述,它涉及“主题”编辑方案。

主题输入屏幕

“主题”屏幕将显示所有当前主题(主题 ID 和主题名称)的一个缩略列表,在每个主题旁边还将显示一个“编辑”链接。单击“编辑”链接将会调用关联的主题记录并将其显示在一系列的输入框中。“标题”和“描述”是可编辑的,而“主题 ID”是只读的。用户可以编辑标题和描述,然后按“保存”按钮将更改写入数据存储。输入将被验证。两者都是必需的输入项,“标题”的长度限制为 30 个字符,“描述”的长度限制为 500 个字符。更新完成后,将显示一条响应消息指出已确认更新;如果更新失败,则显示一条消息指出错误状况。

用户还可以删除现有的主题记录,方法是单击列表中的“编辑”链接,审核显示屏幕上的记录细节后,单击“删除”链接。删除完成后,将显示一条响应消息指出已确认更新;如果更新失败,则显示一条消息指出错误状况。请注意,用户不能删除与现有问题或回答相关联的主题。

此外,用户可以完整地添加新主题记录,方法是在初始显示屏幕上单击“新建主题”链接。将显示“标题”和“描述”输入(不显示 ID 输入)并提供一个“保存”按钮。输入将被验证。两者都是必需的输入项,“标题”的长度限制为 30 个字符,“描述”的长度限制为 500 个字符。更新完成后,将显示一条响应消息指出已确认更新;如果更新失败,则显示一条消息指出错误状况。

利用上面的叙述,您可以轻松地实现一个完整的功能屏幕。判断一个好的设计文档的方法是:它能够使读者完成工作,且不会提出额外的问题。最终的用户界面设计文档将包括应用程序中每个屏幕的此类叙述。

小结并付诸行动

我们简要介绍了数据库、中间层和用户界面实现方案的最终设计文档。加上体系结构和初始规划文档,它们形成了我们的完整设计包。在实际的情况中,即使是最小的系统,完成这些文档也至少需要几个小时。对于大型系统,可能需要几周甚至可能几个月的时间。有些人可能会对此有一点挫败感,但是通过事先完成这些工作,您可以在进入项目的编码阶段之前很早就了解完成解决方案面临的几乎所有主要障碍。这样可以减少编写实际代码的时间,并且还可以减少您会遇到的错误和障碍的数量。

在下一篇文章中,我们将讨论使用 Visual Studio .NET 在 SQL Server 中建立数据存储系统的有关细节。我们将定义数据表,创建必需的存储过程,并设置正确的数据访问,以确保任何组件和数据本身之间具有安全可靠的连接。