分享

Delphi中WebService服务

 A芝兰之室 2017-10-13
Delphi开发和调用的webservice
在用delphi编写webservice时遇到的第一问题就是如何编写,怎么写?上网查了一下,内容还是听不少的,当然通过多方资料的比较,了解到通过file--new----other----webservice可以建立并测试webservice,但是我的delphi中却没有这个选择,于是很迷茫,问了一下“大牛”程序员才知道,是因为我的vcl自带的包被卸掉了,于是从delphi的安装文件的bin目录中找到了相应的包,安装完成后开始webservice的编写和测试。
下面说明如何编写用于IIS发布的ISAPI类型的WebService。
1、 New|Other|WebServices|SOAP Server Application|这里先选择建立Web App Debugger类型的WebService,因为这种类型的WebService便于调试,当我们调试好它,准备发布时再将此类型转换为ISAPI类型。
2、 选择Web App Debugger后,随便输入一个ClassName,这里我们输入“Test”
3、 随后Delphi会询问你是否建立接口单元,选择是,然后输入接口的名字,我们输入Main,Delphi将自动建立接口单元(名字为你输入的接口名+Intf结束,即MainIntf)和实现接口的单元(名字为你输入的接口名+Impl,即MainImpl)。到此一个空的WebService已建立好。
4、 接下来我们将编写供别人调用的WebService函数。在此我们编写一个简单的例子。打开接口单元(MainIntf),在Type后,接口声明后添加接口函数
“function GetMsg(AMsg: string): String; stdcall;”,函数后面必须加上“stdcall”。
   如图:
5、接口函数的声明已经完成,下面就是要实现这个函数了。打开接口实现单元(MainImpl),
   在public中写上该函数的声明,在implement后写该函数的实现。如图:
6、到此,WebService已经撰写完毕。接下来是调试。在我们新建的时候,Delphi已经为我们   建立了一个Unit1和其窗体,在Unit1中引用接口单元(MainImpl),然后在窗体中加一个   按钮,在按钮的单击事件中调用刚才写的WebService函数就可以调试了,代码如图:
或者使用THTTPRIO控件
7、调试成功后就可以转类型了,将Web App Debugger类型转换为ISAPI类型其实很简单,我们先重新建一个ISAPI类型的WebService项目,依次选择New|Other|WebServices|SOAP Server Application|ISPA/…,提示是否创建接口时选择“是”,然后输入和刚才一样的接口名,接着保存好,然后将调试成功的Web App Debugger类型的WebService项目中的接口单元和接口实现单元复制替换掉刚刚创建的ISAPI类型项目中的接口单元和接口实现单元,然后打开ISAPI类型的WebService,编译生成dll。至此ISAPI类型的WebService建立成功。
8、将ISAPI类型的WebService发布到IIS上。在IIS中新建站点,新建时将执行权限设置成“脚本和可执行文件”,将WebService整个项目拷贝到站点文件夹下,启动站点,该WebService就算发布成功了,如果IIS是6.0以上的注意在Web服务扩展中将“所有未知ISAPI扩展”设置为允许,
   具体设置可参见IIS帮助文档。如图:
9、如何用Delphi调用刚才写的WebService。在浏览器中输入刚才站点的路径,如:   http://127.0.0.1/project2.dll,浏览器转到项目所在文件夹,如图:
打开如下图所示的的dll描述页面,该dll中有一个个接口函数GetMsg,它们都是供别人调用的接口函数。点击WSDL打开WSDL描述页面,此时复制该页面的网址http://127.0.0.1/project2.dll/wsdl/IMain,这个网址是我们要用到的。
10、 得到网址后,新建一个Application,我们就在这个Application中调用刚才的WebService。依次点击New|Other|WebServices|WSDL Import,如后提示输入网址,我们输入刚才复制的网址,点击next,finish,此时Delphi将自动添加一个单元,该单元就是调用WebService的单元,有了这个单元我们就可以调用WebService了。在Unit1中引用该单元,再添加一个按钮,在按钮的单击事件中声明一个接口对象,然后调用自动生成单元中的GetMainIntf(该方法是自动生成的)函数给这个接口对象赋值,然后就可以用这个接口对象调用接口函数了。
注意:如果前台使用THTTPRIO控件来连接,
把http://127.0.0.1/project2.dll/wsdl/IMain赋给wsdlLocation属性,而不是赋给URL属性
*******************************************************
Delphi实现WebService带身份认证的数据传输
WebService使得不同开发工具开发出来的程序可以在网络连通的环境下相互通信,它最大的特点就是标准化(基于XML的一系列标准)带来的跨平台、跨开发工具的通用性,基于HTTP带来的畅通无阻的能力(跨越防火墙)。
WebService给我们的软件开发带来了诸多好处,但是有一点还是必须要考虑到的,那就是安全问题。提供Service的一方要控制用户的限制访问,就要对来访的用户进行身份验证。验证成功则继续提供服务,否则就触发无权访问的异常,返回给客户。那么现在我们要解决的问题是这样的:用户的身份认证信息如何在调用主要服务前发送到服务方,从而进行验证?
在WebService中,用户身份认证信息可以在客户端通过soap头(soap header)进行传送。在WebService服务端的编写中,需要对soap头进行处理,这个处理过程就是提取Soap Header中的用户认证信息进行验证。下面就来看看在Delphi中这个身份认证是如何实现的。
一、    自定义的Header类
你需要定义一个用来存放认证信息的类,这个类继承于TSoapHeader。
TAuthHeader = class(TSOAPHeader)
private
  FUserName: WideString;
  FPassWord: WideString;
published
  property UserName: WideString read FUserName write FUserName;
  property PassWord: WideString read FPassWord write FPassWord;
end;
这个类包含了用户名和密码两个属性,当然你可以根据情况增加更多的信息。
再说一下这个类是在哪定义的,它是定义在服务端的接口声明单元。服务发布以后,生成的WSDL中会有这个类的定义,这样在客户端用WSDL Importer导入接口单元的时候,这个类也会自动生成,当然你还要在服务端对这个类进行注册:
InvRegistry.RegisterHeaderClass(TypeInfo(ISoapAuth), TAuthHeader);
RemClassRegistry.RegisterXSClass(TAuthHeader);
ISoapAuth是服务端提供的服务接口。
二、    客户端发送Header
我们还假设ISoapAuth是服务端提供的服务接口,它提供了GetInfo()这么一个服务。
客户端程序片段:
procedure TClientForm.GetInfoButtonClick(Sender: TObject);
var
  aIntf: ISoapAuth;
Headers: ISOAPHeaders;
  H: TAuthHeader;
Begin
  aIntf := (HTTPRio as ISoapAuth);
  H := TAuthHeader.Create;
  H.UserName := ‘piao’ ;  //这里只是举个例子
H.PassWord := ‘840717’; 
Try
  Headers := (aIntf  as  ISOAPHeaders);
  Headers.Send(H);  //发送Soap Header
  aIntf.GetInfo;  //调用服务
finally
  aIntf := nil;
  H.Free;
End;
end;
客户端的工作就是这些了,能否调用服务还要看服务端的处理结果了。
三、    服务端接收处理Header
服务端程序片段:
function TSoapAuth.GetServerInfo: WideString;
var
  Headers: ISoapHeaders;
  H: TAuthHeader;
begin
  Headers := Self as ISoapHeaders;
  Headers.Get(TAuthHeader, TSoapHeader(H)); //先获取SoapHeader
  try
    if H = nil then  //SoapHeader 为空
      raise ERemotableException.Create('No authentication header')
    else
      if not CheckUser(H.UserName, H.PassWord) then   //验证失败
        raise ERemotableException.Create('No acess to call on service!');
  finally
    H.Free;
  end;
  Result := 'Hello World!';
end;
以上,TSoapAuth是继承于TInvokableClass 实现 ISoapAuth 的类。
CheckUser()是用来验证用户是否具有访问权限的函数,在服务端定义。
这只是个简单的返回字符串的服务。
四、    对访问WebService的用户的状态的探讨
事实上客户端在每次调用服务端的服务接口时会重新生成一个对象,发送请求,然后接收返回结果,整个调用过程结束后这个对象就被释放。所以可以说WebService是个无状态的对象,也就不存在用户是否登陆的说法。这样的结果使得我们每次调用服务时就必须做一次用户认证(这个认证可能是查询数据库比对),是比较浪费时间和资源的。
如果一定要在服务端保存用户的登陆状态,那么可以在服务端加一个LogIn()的函数。当用户第一次访问服务时,调用LogIn()记录下用户的状态信息,并且赋给这个用户在一段时间内无限制(是指不必经过CheckUser这个过程)访问服务的权限,当这段时间过后,用户的登陆状态被释放掉,必须重新登陆才能继续调用服务。
至于这个用户状态信息如何在服务端保存,就可能有几种方法了。一是用文件形式保存(xml或ini),二是数据库保存,三是用程序中的变量保存(可以在程序中定义一个UserList的变量来记录用户的状态信息)。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多