发现 ISAPI. XML 方式





4.00/5 (6投票s)
2002 年 9 月 2 日
8分钟阅读

64859

581
使用 XML 和 XSL 构建 ISAPI Web 应用程序的不错方法。
引言
在某些情况下,将所有应用程序数据存储在 Web 服务器的内存中是有意义的,即使是在 Web 场中。如果您的应用程序使用读取-写入状态且更新不频繁,则将页面存储在 Web 服务器内存中可以减轻数据库服务器的负担。
要实现这一点,Web 场中的所有 Web 服务器必须能够访问相同的数据存储,以便将应用程序状态加载到内存中。加载后,Web 场中的所有 Web 服务器在其内存缓存中都将拥有相同的信息。由于数据是只读的,因此在不同 Web 服务器之间传播更新不是问题。如果应用程序状态需要管理员外部更新,所有 Web 服务器都将在收到指示后重新加载其缓存。
微软在其一些非常大的网站上采用了这项技术,包括 MSN™。由于 home.msn.com 上的大部分信息变化不频繁,因此将尽可能多的信息存储在内存中有意义。
即使实际数据可能存在于关系数据库中,将其直接提取到内存中也是有意义的,因为这样做速度快且成本低。何必用多个只读查询来使数据库服务器过载呢?
XML 使服务器和客户端的状态更易于管理。使用 XML,开发人员可以维护状态信息的结构化含义,同时通过标准的 XML 处理程序 API 轻松对其进行操作。
如果应用程序状态以 XML 形式保存在内存中,则执行简单的检索(例如获取所有标题)将是轻而易举的事。使用 XML,您可以利用 XSL 模式来查询应用程序数据并检索所需的信息。您可以通过对 XML 数据执行 XSL 转换来向用户显示应用程序状态的某些部分。
这种方法适用于变化不频繁的只读数据。您需要定期重新加载内存中的 XML 缓存,以考虑底层 XML 的任何更改。但是,当数据库服务器发生某些更改时,可以实现一些技术来直接且即时地更新内存中的 XML 数据。通过在 Web 服务器上使用内存中的 XML,您的数据将更加易于访问和处理。
功能
应用程序架构非常简单。它有 3 个部分:ISAPI 扩展 (WriteHeadlines
)、COM+ 组件 (ArticleNews
) 和数据存储库(文件、Access MDB、SQL Server)。
WiteHeadlines
ISAPI 扩展负责处理 Web 浏览器请求的 HTTP 输出。- 所有业务逻辑和数据都保留在
ArticleNews
COM+ 组件中。这里嵌套了 XMLDOM 文档的集合。WriteHeadlines
扩展向ArticleNews
组件请求 HTML 输出。在这里,我们将利用 XSL 模式来查询应用程序的 XMLDOM 并检索所需的信息。通过对 XML 数据执行 XSL 转换,我们可以向用户显示应用程序的某些部分。 - 数据在系统初始化时从数据存储库加载到 XMLDOM 集合中。由于数据将保留在内存中,因此数据的实际存储位置并不重要:可以是系统文件、Access MDB、SQL Server 等。
由于我们使用 ISAPI 扩展进行 HTML 输出,**无需 ASP 脚本**。因此,如果我们希望获得最快的响应速度、最高的每秒请求数以及 Web 服务器和客户端浏览器之间传输大量 HTML 数据,这是构建站点的最佳方法。
Administration
首先,需要通过**控制面板**将数据加载到 COM+ 中(从文件、Access、SQL Server)。
我们的网页最终看起来应该是这样的
网页包含左、上、中、右四个区域。整个页面实际上是一个静态 HTML 页面,它以 XMLDOM XSL 对象的内存形式嵌套。使用 XML+XSL=HTML,XSL 的主体填充了来自 XMLDOM XML 对象的所需数据。现在我们只有 4 个页面:第一个显示所有标题的页面、一个热门故事页面以及另外两个返回用户选择的故事(所属行业或类别)。我们可以在 XMLDOM 集合中保留更多 XSL,以对应更多的 HTML 页面。
因此,为了管理和扩展此 Web,需要一个强大的管理系统。
通过**控制面板**,我们可以访问集合中保留的所有 XML 和 XSL。数据加载后,我们可以在 IE 预览中看到所有文档,包括节点和语法颜色。
如果我们想扩展 Web 功能或添加新页面,我们将需要添加新的 XSL 模式来查询应用程序数据并检索所需的信息。由于我们已经拥有所有 XML 数据,因此提供了针对 XML 数据的在线模式查询。
输入 XSL 模式字符串后,只需单击 **SelectNodes** 或 **SelectNode** 按钮,即可显示查询结果!
将此新查询字符串放入 ArticleNews
组件的新方法中,从访问该方法的 WriteHeadlines
创建一个新链接,我们就拥有了网站上的一个新页面!
当然,在网站结构准备就绪后,需要更改每个页面的 HTML,添加/更改颜色,添加一些文字、图片、样式表等。这可以通过访问数据存储库(文件、Access 或 SQL Server)或直接访问 XMLDOM 集合来实现。这样,我们将获得对网站的即时更改,而不会中断用户,并且与数据存储库中的更改相对应。
下面是该模块的预览。我们拥有 XML 文档源数据和所有 XSL 文档的数据源。
在这里,可以使用 **SaveXSL** 按钮在内存和数据存储库中直接修改 XSL 文档并保存它们。文档必须是 W3C 格式良好的,因此它会以红色/绿色颜色显示。
实际上,每个 XSL 都是一个 HTML 文档,因此提供了一个 **PreviewHTML** 按钮(它只获取静态 HTML 数据,不包含 XML)。
要获取 **AllHeadlines** 链接页面的预览,我们必须将该 XSL(静态 HTML)页面与 XML 文档结合起来。是的,XML+XSL=HTML!当然,我们必须单击 **TransformNode** 按钮。
编码
WriteHeadlines 扩展
首先,我们需要有一个指向 ArticleNews
组件的指针。这可以通过一个 public
变量来完成。
IHeadlinePtr pItfHeadline;
之后,必须实例化指针并初始化 ArticleNews
XMLDOM 集合(通过加载数据)。管理员可以选择数据存储库:文件、Access MDB 或 SQL Server。只需从相应的链接运行 LoadFiles
、LoadDBmdb
或 LoadDBsql
。这些任务在 fLoadDB
或 fLoadFiles
函数中完成。
hr = pItfHeadline.CreateInstance( L"ArticleNews.Headline.1" );
if ( FAILED( hr ) )
{
*bstrError = L"Error.Attempt to create instance of
TierXmlDom object failed with error";
return false;
}else
{
switch (iType)
{
case DB_SQL:
pItfHeadline->LoadDBsql(L"File Name="+
MapPath(pCtxt,L"Data\\Connection.udl"));
break;
case B_ACCESS:
pItfHeadline->LoadDBmdb( MapPath(pCtxt,
L"Data\\data.mdb") );
break;
}
if ( ! pItfHeadline->DomLoaded() )
{
*bstrError =
L"Component ArticleNews.Headline.1. XMLDOM is not loaded.";
return false;
}
}
它使用 ArticleNews
的 LoadDBsql
、LoadDBmdb
或 LoadDBFiles
方法,具体取决于用户的选择。
Printall
、printhotstories
、printbycategory
、printbyaffiliation
是网站方法,它们将所需的页面返回到客户端浏览器。它们只是相应 ArticleNews
方法的包装器。
if(pItfHeadline == NULL)
{
*pCtxt <<_T("COM component not loaded.")
<<_T("Try Load XMLDOM options in Control Panel.");
return;
}
if (! pItfHeadline->DomLoaded())
{
*pCtxt << _T("XMLDOM not loaded in COM component.");
return;
}
//interogate the XMLDOM
*pCtxt << pItfHeadline->PrintHeadlines();
Addheadline
、printData
、transformNode
、buildXPath
、saveXSL
是 WriteHeadlines
在**控制面板**管理系统中使用的 XSL 方法。这些方法使用 ArticleNews
组件的方法 AddHeadlineBatch
、GetXML
、SaveXSL
以及一些特定的客户端 JavaScript 代码。
一个有趣的功能是 GetPath
函数,它可以自动提供 Web 的实际路径(就像 ASP 中的 MapPath
一样)。
// convert the URL into a useable local path
pCtxt -> GetServerVariable ( "URL", szPath, &size );
pCtxt->ServerSupportFunction ( HSE_REQ_MAP_URL_TO_PATH, szPath, &size, 0 ) ;
另一个辅助函数是 LoadLongResource
,它可以快速提供编码的 HTML 代码。
ArticleNews 组件
内存中存储 XML 信息(XML 和 XSL)的基本单元是 DOM Document。
CComPtr<IXMLDOMDocument2> m_spDomDocHeadlines;
// XML document to hold the headlines
hr = m_spDomDocHeadlines.CoCreateInstance(__uuidof(DOMDocument30));
由于我们的程序支持 3 种数据存储类型,因此它将有 3 对 Load
/Save
方法:LoadFiles
/SaveFiles
、LoadDBsql
/SaveSQL
、LoadDBmdb
/SaveMDB
。
我们的程序有 3 种存储 XML/XSL 信息的方式。第一种方式是为每个 XML/XSL 使用 CComPtr
变量。因此,我们将有一个用于包含 XML 数据的 XMLDOM 文档的 m_spDomDocHeadlines
,以及用于其余所需 XSL 的 m_spDomDocStylesheet
、m_spDomDocStylesheetNode
、m_spDomDocStylesheetIE
。这种方法不太好,因为每次需要新的 XSL(HTML)页面时,我们都必须添加更多代码。第二种方式是一个更好的改进。所有 XSL 文档都托管在一个 STL 集合 - mapDOM
中。它具有用于加载和检索 XSL 的 Put
和 Get
方法。
mapDOM.Put(L"xslAll", m_spDomDocStylesheet); //fill the map
BSTR bstrOut;
//retrieve XSL
hr = mapDOM.Get(bstrName)->get_xml(&bstrOut);
这样,就可以将 XSL 页面添加到 DB 中,并且无需对 COMponent 进行任何更改或重新编译,所有新的 XSL(HTML)页面都将加载到内存集合中。第三种方式只是第二种方法的优化。其思想是关于 TransformNode
方法,该方法将 XML+XSL=HTML 页面。每次请求时,XMLDOM 都会处理特定的 XSL,这是一个耗时操作。为什么不使用 XMLDOM 处理器并保留所有这些已编译的 XSL 文件?我们将有一个新的 STL 集合 - mapProcessor
,具有 Put
和 Get
方法。
// create a temporary instance of DomDoc
hr = spDomDoc.CoCreateInstance(__uuidof(FreeThreadedDOMDocument30));
hr = spDomDoc->loadXML(Value, &bSuccess);
CComPtr<IXSLTemplate> spDOMXSLTpl;
CComPtr<IXSLProcessor> spDOMXSLProc;
spDOMXSLTpl.CoCreateInstance(__uuidof(XSLTemplate30));
spDOMXSLTpl->putref_stylesheet(spDomDoc);
hr = spDOMXSLTpl->createProcessor(&spDOMXSLProc);
mapProcessor.Put( Name, spDOMXSLProc); // third way
要直接在内存中更新 XSL 特定页面,我使用了 SaveDOM
函数,该函数访问我们预处理 XSL 的 mapProcessor
集合。
CComPtr<IXSLTemplate> spDOMXSLTpl;
CComPtr<IXSLProcessor> spDOMXSLProc;
spDOMXSLTpl.CoCreateInstance(__uuidof(XSLTemplate30));
spDOMXSLTpl->putref_stylesheet(spDomDoc);
hr = spDOMXSLTpl->createProcessor(&spDOMXSLProc);
mapProcessor.Put( Name, spDOMXSLProc);
因此,要输出一个 HTML 页面,我们必须在 XML 和指定的 XSL 之间进行 XMLDOM "转换"。现在这非常容易。
bstrName = L"xslAll"; //choose XSL page
mapProcessor.Get( bstrName )->put_input(_variant_t(m_spDomDocHeadlines));
mapProcessor.Get( bstrName )->transform( &iResult );
mapProcessor.Get( bstrName )->get_output( &vOut );
*bstrHeadlines = vOut.bstrVal;
结论
使用 XML 来管理应用程序状态可以维护应用程序数据的结构并执行更复杂的查询(例如按类别、所属行业等打印标题)。XML 缓存方法可以大大提高应用程序性能并减轻数据库服务器的负担。ISAPI 方法使较慢的 ASP 脚本过时,从而成为需要最佳结果的 Web 的最佳选择。
本文受到 Aaron Skonnard 的出色文章——“基于 XML 的持久化行为解决了 Web 场难题”的启发。
许可证
本文没有明确的许可证附加,但可能包含文章文本或下载文件中的使用条款。如有疑问,请通过下面的讨论区与作者联系。可以 此处 找到作者可能使用的许可证列表。