65.9K
CodeProject 正在变化。 阅读更多。
Home

动态构建 Web 界面

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.35/5 (15投票s)

2009年1月26日

CPOL

9分钟阅读

viewsIcon

31563

实际用例:一种灵活的方法,使用 SQL Server、ASP.NET 和 XSLT 来呈现动态内容。

引言

Web 应用程序是动态的——许多应用程序需要为每个用户提供独特的内容和界面。有无数种方法可以将用户内容从数据库中提取并显示在浏览器中。本文重点介绍在 Microsoft 生态系统(特别是 ASP.NET)中实现这一目标的不同方式,我们为什么为自己的产品选择 XSLT,最后,还将介绍我们的技术实现。

规划动态内容

无论您是正在构建新的业务 Web 应用程序还是计划构建一个,您很可能需要解决 Web 应用程序将如何为不同用户显示不同内容的问题。

考虑因素

在考虑显示动态内容的各种选项时,通常需要考虑以下方面:

  • 可用性

    可用性可能是应用程序成功(或失败)的最关键方面之一。

  • 开发时间

    这包括满足当前应用程序要求所需的总开发时间。

  • 灵活性

    无论您当前的需求多么全面,应用程序都会随着时间的推移而演变。评估满足更改所需的开发工作量和技能集非常重要。

  • 支持、维护和持续增强

    许多人在规划新开发项目时通常会忽略这一点,但它通常占应用程序在其生命周期内的总成本的很大一部分。这包括错误修复、客户定制、小型应用程序增强,当然还有 QA 和测试。

ASP.NET 中的选项

通常,使用 ASP.NET 的 Web 应用程序在显示动态内容方面有两种主要选项:

  • 服务器控件
    • 通过绑定 - 检索相关数据并将其绑定到 Web 窗体上适当的 ASP.NET 服务器控件。
    • 通过代码 - 在代码中填充适当的 ASP.NET 服务器控件。
  • HTML
    • 通过代码 - 根据从数据库检索的信息在代码中构建要显示的 HTML。
    • 通过 XSLT - 以 XML 格式检索数据库信息,然后使用 XSLT 将其转换为 HTML。

第三个选项是 Silverlight,这是 Microsoft 大约一年前推出的一项新技术,作为 Web 应用程序的一个选项。Silverlight 提供了非常丰富的 GUI,拥有 .NET 平台(实际上是一个子集)的强大功能以及使 Web 应用程序开发类似于最新 Windows 应用程序开发工具中 XAML 界面的工具。不过,Silverlight 不在本文的讨论范围内。

实际用例:Scopings

当我们开始评估 Scopings(我们自研的招聘平台)的需求时,我们需要一种方式来呈现具有独特外观和感觉的复杂内容,提供高度的可用性,构建一个能够轻松适应频繁且重大的更改的基础结构,并内置全球化功能。

我们很快意识到,虽然我们可以使用 ASP.NET 服务器控件在相对较短的时间内构建 Scopings 的第一个版本,但这将无法很好地适应我们预计将不可避免地成为产品生命周期一部分的频繁更改。此选项将大大增加我们的总体拥有成本。

经过大量分析和讨论,我们清楚地认识到,设计我们自己的使用 XML 和 XSLT 构建 HTML 的基础结构将满足我们所有的需求。我们坐下来开始设计一个最终用于 Scopings 中 80% 以上功能的基础结构。

构建 HTML

Scopings 基础结构背后的理念是允许我们对用户界面进行重大更改,同时消除对任何代码更改和开发人员参与的需求,并大大减少任何用户界面更改后所需的 QA 工作量。

为了满足这些要求,Scopings 基础结构被设计成完全独立于从数据库检索的信息以及信息的最终显示方式。

为实现这一目标,我们设计的基础结构如下:

  • 针对要显示的每个 Web 内容页面构建一个单独的存储过程,并设计该存储过程仅返回 XML 给调用代码。
  • 加载 Web 页面后,代码隐藏将加载当前用户的 XML。然后 XSLT 会将 XML 转换为用户的 HTML 界面。
  • Web 页面将负责所有功能,包括服务器端的代码隐藏和客户端的 JavaScript。
  • 所有样式都使用 CSS 处理。

考虑到上述机制,任何关于我们显示数据的方式或页面内容的变化,都只会涉及以下内容:

  • 如果需要其他数据,我们将修改存储过程以包含所需的新数据。如果不需要额外数据,则不修改存储过程。
  • 修改 XSLT 以包含要显示的新数据以及任何显示更改。
  • 修改相应的 CSS 文件以进行任何样式更改。
  • 测试显示更改和特定页面功能(如果有)。

这使得内容和界面更改所需开发工作量很少或没有,并且可以非常快速地完成,测试工作量也最少。

使用 XSLT 构建 Web 界面

在本节中,我将提供构建简单基础结构所需的技术细节,该基础结构用于从 SQL Server 数据库检索动态内容并使用 XSLT 在 Web 页面上显示它。

如上所述,显示动态内容的过程包括以下步骤:

  • 提取数据库内容 - 构建一个存储过程以 XML 形式输出所有 Web 页面内容。
  • 从数据库检索 XML - 构建一个函数来调用存储过程并检索其数据。
  • 使用 XSLT 将 XML 转换为 HTML - 开发必要的 XSLT 以将从数据库检索的 XML 转换为 HTML。
  • 在 Web 页面上显示内容 - 配置 Web 页面以检索 XML,将其转换为 HTML,然后最终将界面显示给用户。

步骤 1 - 提取数据库内容

提取数据库内容最灵活、最强大的方法是创建输出 XML 的存储过程。这样,只需一次数据库调用即可轻松提取界面所需的所有数据。将来更改内容只需要对相应的存储过程进行微调,而无需更改调用代码。

以下是一个示例存储过程,它构建一个 XML 文档,其中包含特定用户的名字、姓氏以及该用户需要完成的任务列表:

CREATE procedure [dbo].[Get_User_Profile_XML]
@User_ID varchar(10)
AS

SET NOCOUNT ON; 

SELECT Users.[User_ID] AS [@id], 
Users.First_Name AS [@first],
Users.Last_Name AS [@last],

--all user tasks
(
  SELECT Tasks_ID AS [@id],
      Task_Description AS [@description]

  FROM dbo.Users_Tasks

  WHERE Users_Tasks.[User_ID] = Users.[User_ID]

  FOR XML PATH('task'),TYPE, ROOT('tasks')
)

FROM dbo.Users

WHERE Users.[User_ID] = @User_ID

FOR XML PATH('user'), TYPE;

注意:这是针对 SQL Server 2005 编写的,可以轻松调整为使用 `FOR XML EXPLICIT` 以与 SQL Server 2000 一起使用。

上述存储过程的输出 XML 可能如下所示:

<user id="U12" first="John" last="Smith">
  <tasks>
    <task id="T34" description="Annual review"/>
    <task id="T56" description="File my taxes"/>
  </tasks>
</user>

步骤 2 - 从数据库检索 XML 内容

构建适当的存储过程后,我们需要开发可以在运行时调用此过程并提取当前用户所需数据的代码。

由于提高灵活性和降低长期维护成本是我们的主要目标,因此最好将检索数据库信息所需的代码与它返回的内容几乎完全无关。直接从存储过程提取 XML 可以实现这一点。我们可以不断更改 XML 结构和内容,而无需更改调用代码。

以下函数可用于从上述存储过程提取 XML 数据,并将 XML(作为字符串)发送回客户端:

private string GetUserProfileXML(string userId)
{
   const string MY_CONNECTION_STRING = "YOUR CONNECTION STRING";
   const string PROCEDURE = "dbo.Get_User_Profile_XML";

   using (SqlConnection connection = new SqlConnection(MY_CONNECTION_STRING))
   using (SqlCommand command = new SqlCommand())
   {
    connection.Open();

    command.Connection = connection;
    command.CommandText = PROCEDURE;
    command.CommandType = CommandType.StoredProcedure;

    SqlParameter param = new SqlParameter("@User_ID", SqlDbType.VarChar, 10);
    param.Value = new SqlString(userId);
    command.Parameters.Add(param);

    using (XmlReader reader = command.ExecuteXmlReader())
    {
      reader.MoveToContent();
      string resultXml = reader.ReadOuterXml();
      return resultXml;
    }
  }
}

注意:这会打开与 SQL Server 的连接,配置命令对象及其 @ID 参数,并从 `XmlReader` 读取 XML 作为字符串。

步骤 3 - 使用 XSLT 将 XML 转换为 HTML

我们的下一个任务是将 XML 转换为可以显示给当前用户的 HTML。

在将 XML 转换为 HTML 方面,XSLT 非常实用且是理想的解决方案。XSLT 提供了巨大的灵活性,可以快速调整以处理 XML 内容的任何更改或新需求。

XSLT 文件可以轻松地作为任何 Web 应用程序的一部分包含,虽然这超出了本文的范围,但开发团队可以选择稍微调整此方法,通过为支持的每种语言开发不同的 XSLT 文件来全球化其 Web 应用程序。

以下 XSLT 可用于处理上述 XML 数据:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>

  <xsl:template match="user">
    <div class="userInfo">
      <xsl:value-of select="concat('Tasks for ', @first,' ',@last,':')"/>
    </div>

    <div class="userTasks">
      <xsl:for-each select="tasks/task">
        <div class="userTask">
          <a>
            <xsl:attribute name="href">
              <xsl:value-of select="concat('taskinfo.aspx?id=',@id)"/>
            </xsl:attribute>

            <xsl:value-of select="@description"/>
          </a>
        </div>
      </xsl:for-each>
    </div>
  </xsl:template>
</xsl:stylesheet>

注意:这会输出页面的标题(名字和姓氏),然后输出所有相关的用户任务。

HTML 结果可能如下所示:

<div class="userInfo">John Smith Tasks</div>
<div class="userTasks">
  <div class="userTask"><a href="taskinfo.aspx?id=T34">Annual review</a></div>
  <div class="userTask"><a href="taskinfo.aspx?id=T56">File my taxes</a></div>
</div>

步骤 4 - 在 Web 页面上显示内容

最后一步是创建处理和显示适当用户内容所需的 Web 页面。如文章开头所述,我们需要通过调用我们之前开发的“`GetUserProfileXML`”函数从数据库检索 XML,使用上述 XSLT 文件将 XML 转换为 HTML,然后最终将 HTML 显示给用户。

我们首先创建一个带有服务器端 `DIV` 的 ASPX 页面,该 `DIV` 最终将包含 HTML。页面可能如下所示:

<%@ Page Language="C#" AutoEventWireup="true" 
    CodeFile="userprofile.aspx.cs" Inherits="UserProfile" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>My User Profile Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div id="_divData" runat="server">
    </div>
    </form>
</body>
</html>

注意:`DIV` 被设置为在服务器上运行,以便可以在服务器上分配 HTML。

接下来,我们需要开发一个函数,该函数接受 XML 并使用 XSLT 文件将其转换为 HTML。

接受 XSLT 文件名和 XML 数据并执行转换的函数可能如下所示:

private string GetPageHTML(string xsltFileName,string xmlData)
{
    string fullXsltFilePath = Server.MapPath("~/" + xsltFileName);

    using (XmlReader dataReader = LoadXMLToReader(xmlData))
    {
        XslCompiledTransform xslTrans = new XslCompiledTransform();
        xslTrans.Load(fullXsltFilePath);
    
        using (MemoryStream outputStream = new MemoryStream())
        {
            xslTrans.Transform(dataReader, null, outputStream);
    
            outputStream.Position = 0;
    
            using (StreamReader sr = 
                   new StreamReader(outputStream, Encoding.UTF8))
            {
                string resultHtml = sr.ReadToEnd();
                return resultHtml;
            }
        }
    }
}

注意:此函数获取 XSLT 文件的完整路径,将 XML 数据加载到 `XmlReader` 中,然后使用 `XslCompiledTransform` 对象将 XML 转换为 HTML,并将 HTML 作为字符串返回给调用代码。此外,`XslCompiledTransform` 是一个线程安全对象,因此我强烈建议缓存它以进一步提高网站性能。

最后,为了把所有内容整合起来,Web 页面上的 `Page_Load` 函数可能看起来像这样:

protected void Page_Load(object sender, EventArgs e)
{
  const string XSLT_FILE_NAME = "UserProfile.xslt";
        
  //gets the current user id
  string userId = "U12";

  //loads the xml data from the database
  string xmlData = GetUserProfileXML(userId);

  //transform the XML into HTML
  string html = GetPageHTML(XSLT_FILE_NAME, xmlData);

  //shows the html to the user
  _divData.InnerHtml = html;
}

注意:用户 ID 是硬编码的,但它应该根据当前登录的用户动态检索。

结论

Web 应用程序的动态内容可以通过多种方式生成,但如果您正在构建一个具有高度灵活性、高可用性且总体拥有成本低的 Web 应用程序,那么使用 XSLT 将 XML 转换为 HTML 界面可能是一个正确的方法。

© . All rights reserved.