创建网站服务(Web Services)
网站服务(Web Services)是基于一些标准的(通常是 SOAP),使应用程序能够通过HTTP 交换数据。网站服务由网站方法(web methods)组成,公开这些方法,可以在网络上运行;某种程度上,可以把它看作是 F# 的函数,因为网站方法有名字,有参数,也返回结果,参数和结果用元数据(metadata)进行描述,网站服务把这些也都公开了,因为客户端可以知道如何调用它。
创建网站服务非常简单。事实上,创建网站服务的主要问题可能是如何通过网站服务器公开代码。网站服务器以URL 的形式接收对文件的请求,就必须告诉网站服务器,这个请求应该映射到哪一个 .NET 类。通常用 .asmx 文件运行特定的 F# 类,响应对这个网站服务的请求,如果网站服务器获得对这个 .asmx 文件的请求。准确的实现方法依开发环境和托管服务的网站服务器的不同而不同。
Visual Studio 有内置的网站服务器,因此,创建新网站站点,只需要简单地选择文件->新建->网站,然后,选择网站的位置。站点只能运行用 C# 或者 Visual Basic .NET 写的页面,因此,还需要在解决方案中添加 F#项目,然后,手工修改解决方案文件,以便它驻留在网站目录中。听上去复杂,做起来很容易,只要把 .fsharpp 文件复制到网站目录,用记事本打开 .sln 文件,修改 .fsproj 文件的路径。图 11-3 是修改前后的 .sln 文件,左边是原始文件。
图 11-3 如何修改 .sln 文件
之后,需要配置项目文件的输出为类库,并写到 bin 子目录。这似乎要一定的努力,但以后只要按下 F5 就行了,项目会自动编译并运行。
[
1、输出为类库,这一项只要确认一下就可以了,因为在添加项目时会要求选择的,因此,只有当时没有选择类库时,现在才要修改;
2、写到 bin 子目录,需要在后期生成事件命令行中输入:
copy $(TargetFileName)E:\WebSites\ASP.NET\bin
这里假定E:\WebSites\ASP.NET 是网站驻留的目录,且已经新建了 bin 子目录。
]
如果没有 Visual Studio,那么下一步要做的事情最好就是用互联网信息服务(Internet Information Services,IIS)托管站点,微软自己的用于 Windows 的网站服务器。在某种意义上,这个比用 Visual Studio 托管更容易,但对于完成代码后的运行并不方便。为了在 IIS 中托管代码,需要创建一个 IIS 的虚拟目录,和子目录 bin;然后,复制 .asmx 页面和 web.config 文件到这个虚拟目录中。
服务本身很简单。它应该就是一个类,继承自System.Web.Service.WebService,有一个无参数的构造函数;也应该把它标记成 System.Web.Service.WebServiceAttribute。如果打算公开网站服务,必须设置属性的命名空间(Namespace),默认是http://tempuri.org;即使不打算公开服务,设置这个属性对网站服务的可管理性也有好处。那么,类的成员,即网站方法,用System.Web.Service.WebMethodAttribute 标记[ 原文 WebServiceAttribute有误],它也有大量有用的属性,特别值得设置的是描述(Description)属性,它能使服务的客户端知道可以获得什么。
清单 11–7 定义了一个简单网站服务,创建了类型 Service,它有一个成员Addition 的服务,参数必须以元组的风格出现。
清单 11-7 创建一个简单网站服务
namespaceStrangelights.WebServices
open System.Web.Services
[<WebService(Namespace =
"http://strangelights.com/FSharp/Foundations/WebServices")>]
type Service =
inherit WebService
new() = {}
[<WebMethod(Description ="Perfomsinteger addition")>]
member s.Addition ((x : int), (y : int)) = x + y
[
需要引用 System.Web、System.Web.Services、System.EnterpriseServices;
把x.Addition 改成 s.Addition;当然,也不一定是 s,只要不与后面出现的 x 和 y 相同的字母都是可以的。
]
要想让网站服务器发现网站服务,需要创建 .asmx文件。下面是 .asmx 文件的示例,最重要的是把 Class属性设置为服务的类名。当服务器收到对这个文件的请求,就会调相应的服务:
<%@ WebServiceClass="Strangelights.WebServices.Service" %>
如果在本地运行这个服务,只要简单地在浏览器中打开它,就能测试了。在浏览器中,将看到如图 11-4 所示的界面,可以为网站服务提供参数值,并调用这个服务。
图 11-4 调用本地的网站服务
用 46 和 28 作为参数值,调用这个服务,产生下面的 XML:
<?xml version="1.0"encoding="utf-8" ?>
<intxmlns="http://strangelights.com/FSharp/Foundations/WebServices">74</int>
通常,在网络上发送少量的数据是效率低下的,因为发送的每个请求都必须有固定数量的元数据。因此,更好的办法是构建的应用程序不能是“闲聊式”的,即应用程序应该发送一个大的请求,而不应该发送许多个小的请求。
网站服务将企图把任何 .NET 对象的方法返回值序列化(serialize)成 XML;然而,结果可能有点无法预测,因此,结果的 XML 数据也不可能包含所有期望的字段,这样,就很难在其他编程语言中使用了。要避免这种情况,最好是使用 XSD 架构来定义想要跨网站服务传递的对象。XSD 架构是一种 XML 文档,它描述了 XML 文档的结构,描述的内容包括哪些字段是强制性的,以及字段应该按什么顺序出现等、这些架构会成为网站服务定义的一部分,使用这个网站服务的所有的人都能了解他会从这个网站服务期望得到什么字段;定义的网站服务中有一个字段包含二进制或十六进制数据,是最好的,因为用为网站服务的用户有更多的机会理解结果的含义。
虽然自己定义 XML 架构是可能的,但是,Visual Studio 有一些创建架构的图形方式和文本方式;网上有许多预定义的架构可以下载,而在些基础上再构建也是可能的。例如,下面将要看到的示例使用了RNAML,这个在http://www-lbit.iro.umontreal.ca/rnaml/上可以找到,这是为想以 XML 形式交换 RNA(核糖核酸,一种由分子生物学家研究的物质)信息的人们提供的架构。从网站上下载架构以后,就可以用使用命令行工具 xsd.exe 把架构转换成 .NET形式的表示。这个工具生成以# 类来表示 XML 数据,通常,XML 中的每个标记都会成为 .NET 的一个类;然后,可以把这个 C# 文件编译成一个 .NET 程序集,供 F# 或其他任何 .NET 库使用。完成的命令行可能像这样:
xsd rnaml.xsd /classes
注意,需要把下载的架构文件rnaml.xml 重命名为 rnaml.xsd,工具才能正确运行。
下面的示例演示如何创建网站服务,返回酵母 RNA 分子结构,这是 RNAML 网站上系列示例的缩减版本(http://www-lbit.iro.umontreal.ca/rnaml/)。像以前一样,需要创建一个 .asmx 文件,把被请求的文件链接到 .NET 类型上,来处理请求:
<%@ WebServiceClass="Strangelights.WebServices.DnaWebService" %>
清单 11-8 展示了这个网站服务,可以看到,这个网站的基本组件与前面简单的网站服务是相同的。像前面的一样,有一个类定义,标记为WebService 特性;代码所做的工作是定义了方法GetYeastMolecule,在这里,我们创建并发布了不同的对象,它们定义在用 rnaml.xsd 文件创建的库中,比如分子对象和序列对象。
清单 11-8 创建网站服务,返回 RNA 分子的定义
namespace Strangelights.WebServices
open System.Web.Services
//the web service class
[<WebService(Namespace=
"http://strangelights.com/FSharp/Foundations/DnaWebService")>]
type DnaWebService() =
inherit WebService()
// the web method
[<WebMethod(Description = "Gets arepresentation of a yeast molecule")>]
member x.GetYeastMolecule () =
// the code that populates the yeast xml structure
let tax = new taxonomy(domain = "Eukaryota", kingdom ="Fungi",
phylum = "Ascomycota", ``class`` = "Saccharomycetes",
order = "Saccharomycetales",
family = "Saccharomycetaceae",
genus = "Saccharomyces",
species = "Saccharomycescerevisiae")
let id = new identity(name = "Saccharomycescerevisiae tRNA-Phe",
taxonomy = tax)
let yeast = new molecule(id = "Yeast-tRNA-Phe", identity = id)
let numRange1 = new numberingrange(start = "1", Item ="10")
let numRange2 = new numberingrange(start = "11", Item ="66")
let numSys = new numberingsystem(id="natural", usedinfile=true)
numSys.Items <- [|box numRange1; boxnumRange2|]
let seqData = new seqdata()
seqData.Value <- "GCGGAUUUAGCUCAGUUGGG AGAGCGCCAG ACUGAAGAUC
UGGAGGUCCU GUGUUCGAUC CACAGAAUUCGCACCA"
let seq = new sequence(numberingsystem = [|numSys|], seqdata =seqData)
yeast.sequence <- [|seq|]
yeast
另外,同样简单,可以使用基于网站的测试方法,运行前面的代码,产生如下的 XML:
<?xmlversion="1.0"encoding="utf-8"?>
<moleculexmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"id="Yeast-tRNA-Phe">
<identity>
<name>Saccharomycescerevisiae tRNA-Phe</name>
<taxonomy>
<domain>Eukaryota</domain>
<kingdom>Fungi</kingdom>
<phylum>Ascomycota</phylum>
<class>Saccharomycetes</class>
<order>Saccharomycetales</order>
<family>Saccharomycetaceae</family>
<genus>Saccharomyces</genus>
<species>Saccharomyces cerevisiae</species>
</taxonomy>
</identity>
<sequence>
<numbering-systemid="natural"used-in-file="true">
<numbering-range>
<start>1</start>
<end>10</end>
</numbering-range>
<numbering-range>
<start>11</start>
<end>66</end>
</numbering-range>
</numbering-system>
<seq-data>GCGGAUUUAGCUCAGUUGGG AGAGCGCCAG ACUGAAGAUCUGGAGGUCCU GUGUUCGAUC CACAGAAUUC GCACCA</seq-data>
</sequence>
</molecule>
这个示例返回一个静态的、不变的 XML 文档,并没有什么特别的用处,但是,很容易看出这种程序的潜力。如果不使用GetYeastMolecule 方法,而是用更现实的方法 GetMolecule,把分子的名字作为参数,就可以在数据库中检索出这个分子的详细信息,返回分子数据的结果。其好处是,运行在任何平台上程序都可以使用这个结果数据。现在,示例站点已经提供了 C++ 和 Java 处理数据的编程接口;我们也已经看到,在 F# 中处理这种数据非常简单。当然,这项技术并不限于分子生物学,可以发现使用 XML 架构几乎所有领域都适用,科学、工程、数学和金融。
可以有几种方法加强网站服务的保护,保护网站服务可以保证只能有部分用户访问,或者在网络上传输的信息要加密或签名。一种方法是升级到 Windows 通信基础(Windows Communication Foundation,WCF),它类似于网站服务,但在这方面提供了更多的灵活性,将在下一节中讨论;另一种方法是我们自己配置网站服务器,处理这些安全需求。
[
需要引用 System.Web、System.Web.Services、System.EnterpriseServices,以及rnaml;
但是,由于没有下载到 rnamk.xml,因此,后面的工作也就没法进行了。
]