生态学家的 Web 服务






4.20/5 (8投票s)
用于在 Web 上分发科学模型的框架
引言
本文介绍了ATL Web服务(Visual Studio 2005 IDE)和Web服务客户端(ASP.NET)的创建过程。最终目标是描述一种简单、高效且可重复的方法,将遗留的C++代码(数学模型和分析)集成到Web服务中,以便这些模型能够通过任何Web浏览器实现(对模型代码的更改最少)。遗留代码是指很久以前编写但仍然有用且不希望用另一种语言重写的代码。
生态学与万维网
生态学(研究动植物之间相互作用的学科)正日益成为一门量化学科。当今的生态学家与在乡间考察动植物一样,很可能在电脑前工作。其中大部分工作涉及创建数学模型和实现它们的软件。
生态模型的研究取得了成功,许多模型被用于支持对经济和社会重要的生态问题的实际决策。例如,用于管理有害入侵物种传播的决策支持工具,记录濒危物种的位置和状态,以及识别动植物物种。
构建模型、将其实现为软件产品并将其部署给多个最终用户的一般过程存在许多常见问题,而本文中使用的技术正是为了解决这些问题。
1) 模型通常是多年“断断续续”资助的结果。因此,构建模型和/或实现代码的人员经常会离开项目。尽管模型应该记录良好,但重新创建、重写和重新验证它们通常是昂贵或不切实际的。解决方案:寻找可以集成遗留代码和现代部署技术的方法。
2) 决策支持工具通常是集成模型或分析的“套件”。例如,“分析”的第一步可能是通过查询公共和/或私有数据源来获取执行分析所需的数据。然后可以使用这些数据来运行一个模型。然后,第一个模型的结果可能会被第二个模型使用。每个模型都可以实现为独立的程序或一组开源类或函数。解决方案:开发能够集成不同模型并允许使用标准化接口从同一GUI调用它们的软件。
3) 研究生态学家越来越多地作为大型团队的一部分工作。与大多数团队一样,每个成员都有特定的优势、劣势和职责。这尤其适用于编程技能。虽然许多生态学家精通一种或多种计算机语言,但他们通常不是计算机科学家。此外,生态学家经常从事多个项目或团队,从事相同类型的工作,但用于不同的生态应用。相同的分析(例如查找特定类型的数据)通常是许多项目共有的。解决方案:采用模块化方法。将项目团队分为负责编写分析的人员(根据生态学专业进行进一步细分)和负责最终用户最终交互的GUI的人员。
4) 许多生态模型的成功并非取决于其好坏或预测能力,而是取决于谁使用它们、它们的使用难度以及它们提供了多少决策支持。项目资金通常与生态问题的规模成比例,因此资金充足的项目可能有大量利益相关者(最终用户)渴望使用决策支持工具。最终用户可能分布在世界各地,可能使用各种操作系统(通常有使用限制),并且具有不同程度的生态专业知识和计算经验。解决方案:使用WWW。使最终产品易于使用(标准外观和感觉),易于升级和维护。
考虑到这些问题和解决方案,我们决定探索一个由C#浏览器代码消耗的ATL Web服务的潜力。由于我们的大多数生态模型都是用非托管C++编写的,我们决定使用Microsoft Visual Studio的ATL Web服务项目,这样我们就无需担心混合托管和非托管代码。对于客户端应用程序,我们使用了C#和ASP.NET。我们将调查的成功建立在以下几点上:
1) 我们能让它工作吗?
2) 非专家编码员构建Web服务的难易程度(以及其功能限制)。
3) 非专家独立编码员(在Web服务编码员的最小交互下)在Internet Explorer和Mozilla浏览器中将其实现为客户端网页的难易程度。
为了研究这个过程,我们将假设我们有一个遗留C++编码的生态模型(在本例中是一个简单的随机数生成器),该模型是用C++类(或类集合)编写的,我们拥有C++代码。在本文中,我们虚构的“模型”非常简单,它只返回一个缩放后的随机数字符串。在这种情况下,模型输出很容易仅使用C#和ASP.NET实现(所以请不要评论这一点!),但很容易想象,对于更复杂的模型,这可能不那么实用。使用代码
本节将详细介绍如何创建Web服务,部署Web服务以及创建调用Web服务的客户端。
步骤1:编写Web服务服务器应用程序
1) 创建一个名为“RandomNumberService”的新ATL Server Web服务。Visual Studio将创建2个新项目
a. RandomNumberService
b. RandomNumberServiceISAPI
2) 在RandomNumberService项目中,展开源文件和头文件文件夹,导航到RandomNumberService.h。此文件包含可以更改以自定义Web服务的骨架代码。Visual Studio自动生成的代码将包含HelloWorld(..)作为示例SOAP函数。函数的声明在此
// HelloWorld is a sample ATL Server web service
method. It shows how to// declare a web service method and its in parameters
and out-parameters
[id(1)] HRESULT HelloWorld([in] BSTR bstrInput, [out, retval]
BSTR *bstrOutput);
// TODO: Add additional
web service methods here
实现可以在CRandomNumberServiceService类的实现中找到
// This is a sample web service method that shows how to use the // soap_method attribute to expose a method as a web method
[ soap_method ]HRESULT HelloWorld(/*[in]*/
BSTR bstrInput, /*[out, retval]*/ BSTR *bstrOutput)
{
CComBSTR bstrOut(L"Hello ");
bstrOut += bstrInput;bstrOut += L"!";
*bstrOutput = bstrOut.Detach();
return S_OK;
}
// TODO: Add additionalweb service methods here
3) 假设我们有一个C++类(或多个类)来表示我们要通过Web分发的模型。在这种情况下,我们使用CRandomNumbers类作为模型(或任何其他代码)的简单、微不足道的示例,该模型可能已独立于Web服务开发。此类包含一个GenRandomNumbers(..)函数,该函数生成一系列均匀随机数并将其作为制表符分隔的字符串返回(它还包含另一个应暂时忽略的GetGraph(..)函数)。
CString GenRandomNumbers(double min, double max, int N)
4) 按照Visual Studio生成的代码注释,在HelloWorld()下方声明另一个Web服务方法GenRandomNumbers(..)。在这种情况下,我们想要一个镜像我们“模型”代码中声明的函数的函数。因此,Web服务方法的声明是
[id(2)] HRESULT GenRandomNumbers([in] double min,[in]double, max, [in] int N,[out, retval] BSTR *bstrOutput);
实现为
[ soap_method ]
HRESULT GenRandomNumbers(double min,double max, int N, BSTR *bstrOutput)
{
////Function that calls a function from an 'imaginarily' complex class and stroes the results in a string
CRandomNumbers random;
CString randString;
randString = random.GenRandomNumbers(min, max, N);
/////randString now contains the string of random numbers
CComBSTR bstrOut(randString);
*bstrOutput = bstrOut.Detach();
return S_OK;
}
5) Web服务现在包含一个调用外部类的方法,该类将随机数字符串作为CString返回。此CString的内容随后被转换并返回给BSTR,BSTR被封装在SOAP消息中并发送给任何调用GenRandomNumbers(…)的客户端程序。
6) 在构建和部署Web服务之前,还有一个重要的最后一步——需要对“Visual Studio的ATL Server项目”中处理WSDL文档请求的自动生成代码进行一些更改。此WSDL文档描述了Web服务服务器公开的方法,并使构建Web服务客户端变得容易。然而,Visual Studio生成的自动代码以与WWW SOAP标准不同的格式创建文档。以下代码行解决了此问题
request_handler(name="Default",sdl="GenWebserverWSDL"),
soap_handler(name="RandomNumberServiceService",namespace="urn:RandomNumberServiceService",
protocol="soap",
style = "document",//add this argument
use = "literal" //add this argument)
7) 现在构建项目。这将创建2个*.dll文件:RandomNumberService.dll和RandomNumberServiceIsapi.dll。转到第2步部署Web服务服务器。
步骤2:部署Web服务服务器
1) 在要部署Web服务的计算机上安装IIS。
2) 创建一个新的虚拟目录(在本例中为RandomGenerator),该目录将包含您的Web服务,并将以下文件复制过去
a. RandomNumberService.dll和RandomNumberServiceIsapi.dll,位于项目已发布的或调试目录中。
b. RandomNumberService.htm,位于RandomNumberService目录中。
3) 打开Web浏览器并导航到您已复制到虚拟文件夹中的*.htm页面,例如:
https:///RandomGenerator/RandomNumberService.htm
4) 如果此文件显示正确,则表示到目前为止一切顺利。*.html页面应显示您刚刚创建的Web服务的基本详细信息。它将显示可用方法的列表(在本例中,它将仅显示HelloWorld,因为我们没有手动添加自定义方法)。更重要的是,它将包含一个指向Web服务的“服务描述”或WSDL文档的链接(实际上是链接到RandomNumberService.dll,它会响应发送描述Web服务的WSDL文件——例如,https:///RandomNumberService/RandomNumberService.dll?Handler=GenRandomNumberServiceWSDL)。单击“服务描述”链接。如果浏览器显示WSDL文件,则一切进展顺利,您可以进入“步骤3-创建Web服务客户端”。如果不是这样,最可能的原因是您创建的虚拟目录尚未设置为处理dll。尝试使用虚拟目录的属性对话框(右键单击虚拟目录并选择“属性”)执行以下清单(一次一个):
a. 在“虚拟目录”选项卡中,确保“执行权限”设置为“脚本和可执行文件”。
b. 在“目录安全”选项卡中,单击“编辑”按钮,然后启用“匿名访问”和“允许IIS控制密码”。
c. 在“虚拟目录”选项卡中,单击“配置”按钮,这将打开一个新对话框。导航到“映射”选项卡。检查扩展名.dll和.srf是否已注册。如果未注册(在我们的一个安装中未注册),则添加它们。
步骤3:创建Web服务客户端
在本例中,我们将使用C#和Visual Studio创建一个基于Web的客户端。使用Web服务的一个优点是几乎任何语言和/或软件都可以用来调用其方法并显示结果。例如,它们可以从任何*.exe程序调用;从Applet调用,或者像这里一样从ASP.NET网页调用。我们选择了这个选项,因为它符合本项目目标,即将一些C++代码(代表现有模型)包装到Web服务方法中,以便模型可以运行,并且结果可以在WWW上获得。
1) 打开Visual Studio(2005)的一个副本。创建一个新的网站项目(文件->新建->网站。在出现的对话框中选择“ASP .NET Web Site”选项。给项目命名(在本例中为RandomClient),并将语言设置为C#。按OK创建项目。项目将创建一个单独的网页-Default.aspx。
1) 下一步是创建使网页能够调用Random Web服务方法的代码。首先将WSDL文件副本保存到包含Web服务的文件夹中。然后从Visual Studio菜单中选择网站->添加Web引用。在出现的对话框的URL文本框中,键入WSDL的URL(例如,https:///RandomNumberService/RandomNumberService.wsdl)。按GO,将显示WSDL描述的方法。为Web服务引用选择一个名称(例如,RandomService)并按OK。两个文件将添加到您的项目App_WebReferences下:RandomNumberService.wsdl和RandomNumberService.discomap。这两个文件是Visual Studio用来创建自动生成代码的文件,使调用Web服务方法变得容易。现在我们要做的就是编写少量的C#代码来调用这些方法。
2) 与Default.aspx页面关联的是Default.aspx.cs文件,其中包含页面的代码。导航到此文件。在所有“using”语句的下方添加以下内容:
using System;using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; //Add this line of code: using RandomService;
其中RandomService是您在步骤2中为Web服务引用指定的名称。然后,在类声明中,添加以下代码:
public partial class _Default: System.Web.UI.Page { String formattedstring; protected void Page_Load(object sender, EventArgs e) { RandomService.RandomNumberServiceService theservice; theservice = new RandomService.RandomNumberServiceService(); formattedstring = theservice.GenRandomNumbers (0,10, 10); //do any amount of formatting on the string so that it //can be displayed nicely on a web-page e.g. //formattedstring.Replace("\n","<BR>"); } }
此代码向名为formattedstring的页面添加一个成员变量(类型为string)。在Page_load事件处理程序中,创建自动生成的Web服务类的一个实例,该实例用于调用GenRandomNumbers(…)方法,并将结果存储在String formattedstring中。
3) 最后,在网页中显示随机数字符串。为此,请导航到Default.aspx页面并修改自动生成的代码,使其如下所示:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"%>
<!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>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<strong>
Random Numbers
Produced by Web Service:<br />
</strong>
<%=formattedstring%>
<br />
</div>
</form>
</body>
</html>
4) 最后,保存所有文件,并通过Web浏览器导航到Default.aspx网页。页面应显示如下内容:
已生成十个介于0到10之间的随机数(根据代码文件中硬编码的Web服务方法调用)。如果刷新页面,将显示新一组数字(遵循对服务的又一次调用)。在这种情况下,数字(来自我们虚构模型的输出)显示得非常简单,但很容易看出字符串是如何在ASP.NET代码中或在Web服务端格式化以产生更好看的结果的。同样,很容易看出在从Web表单收集一些输入后,如何调用Web服务方法。
至此,项目基本目标已实现。本文已演示如何:
a) 将C++代码(代表我们不想重写的复杂模型)包装到ATL Web服务中。
b) 实现一个基于Web页面的客户端,该客户端通过Internet远程调用此模型,接收一些输出数据并在网页上显示它。
有了这个基本框架,项目就可以从多个方面进行扩展。最重要的是,可以创建随机数的简单、微不足道的示例,替换为更重要和更有用的模型。客户端可以开发成一个愉快的用户界面,收集用户输入,调用Web服务并以漂亮的GUI显示结果。最后,Web服务可以向客户端发送不同类型的数据。例如,在示例项目中,我们包含了一个使用C++代码创建随机数简单图的方法(再次假设这是更复杂的C++编码的生态模型输出),并在Web服务中包含了一个将位图字节(作为字节数组)发送到基于Web的客户端的方法。一旦字节数组被传递到客户端,它只需将其重新组装为位图并在网页上显示(同样,只需几行代码)。使用这种方法,视频、GIS、包含数据的Excel文件或任何其他信息都可以通过Web服务发送。这有可能大大增强模型结果的解释和传播。下图显示了一个更复杂的网页,其中包含文本字符串的格式和位图的显示。
关注点
Web服务(尤其是ATL)的文档很差。我们希望,至少,我们的文章能激励其他研究人员深入研究Web服务并查找一些文档。ATL文档差可能是由于新技术(.NET Web服务等)的发展。我们使用ATL是因为我们不想混合托管和非托管代码(我们希望我们的遗留代码按原样编译)。重要的是要记住,无论我们发现这些方法多么有用,可能还有许多其他可用的替代方案。有关示例,请参阅其他CodeProject文章。
特别是,我们在ATL Web服务与现代WSDL解析器(例如Microsoft Visual Studio的、Eclipse)之间的交互中发现了一个令人恼火的细微差别。Visual Studio中ATL向导生成的默认代码创建的WSDL文件与W3CSOAP标准不兼容。默认情况下,WSDL文件以rpc/encoded样式创建。只需在代码中进行微小的更改,即可将样式更改为document/literal,并将WSDL转换为适合现代WSDL解析器的格式(请参阅创建Web服务的第6步)。只需一点点代码……花了三天时间来回查找晦涩的SOAP、Visual Studio、Eclipse以及我们能找到的任何其他文档。如果本文的唯一目标是为这个问题提供警告和修复,我们将非常高兴。
IIS有点奇怪。在我们的开发机上,Web服务服务器的*.dll文件第一次就能工作。然后,我们切换到另一台机器,设置了IIS,安装了*.dll文件,但什么都不起作用。最终,我们发现问题出在IIS没有正确注册*.dll和*.srf扩展名。我们仍然不确定为什么IIS在一台计算机上的设置与另一台计算机不同,但由于IIS中用于注册这些扩展名的工具有一个错误,使得添加映射看起来是不可能的(OK按钮被禁用),因此解决了这个问题。请参阅在线文档:
http://support.microsoft.com/kb/317948
总的来说,我们在项目几乎每一步中都遇到的一个令人困惑的问题是,为什么WSDL文档必须如此复杂?SOAP、Web服务和WSDL的概念似乎很简单。Web服务服务器从客户端接收一个信息“包”,并将信息返回给客户端。这些信息采用一致、标准化的数据格式,因此可以忽略语言和平台差异进行通信。因此,WSDL文件代表了服务位置、它接受的输入以及它返回的输出(包括数据类型)的明确描述,以便任何人都可以实现自己的客户端。无疑是一个绝妙的主意。但为什么WSDL必须如此复杂?难道不能像手动编写WSDL一样简单(以克服解析器的问题),并且在第一眼就能提供服务概述吗?我们看到的WSDL文档只有一堆难以阅读的标签和缩写。
总的来说,我们得出结论,Web服务满足了我们在本文开头为自己设定的要求:
1) 它们使我们能够将旧的C++代码包装到允许模型结果通过Web分发的代码中。考虑到Visual Studio提供的向导,这个过程相对容易,尽管我们仍然可能会遇到一些问题(例如,混合ATL和MFC代码)。
2) 它们可以非常容易地构建基于Web的客户端。此外,一旦知道了Web服务公开的方法,任何VB、Java或C#程序员都可以构建这些客户端。可以构建不同的客户端(可能针对不同的客户),它们都调用相同的Web服务,但获取输入和显示结果的方式略有不同。
历史
目前还没有。