Delphi 写 WebService 架构的三层程序如何维护 Session

问题由来:

如果是基于 TCP/IP 长连接的远程服务,比如 Delphi 新版推荐的 DataSnap,它支持基于客户端和服务器端的 TCP 长连接,服务器端甚至可以通过这个连接回调客户端,向客户端发起主动通知。那么,一个 Session 就是一个这样的长连接。

但是,WEB 访问,是短连接。一次操作,就建立一次连接,操作完成,连接断开。下次操作,重新连接。因此,假设用户登录过后,继续其它操作,服务器端如何知道下次操作的这个连接还是上次登录的那个客户端?

在使用网页访问的 WEB Server 上面,有几种做法:

1. 使用 Cookie 去追踪客户端;

2. 在网页的 Form 里面使用隐藏的字段,也就是不显示在页面上,但页面前端 HTML 代码里有,页面提交后,服务器端可以读到这样的隐藏字段的内容,知道这个页面是之前登录的谁;

3. 使用 URL 参数。类似 http://abcd.com/readDoc?docNumb=123456;  这里问号后面就是 URL 的参数。很多网站使用这样的方式。

4. 可能还有其它什么方式我不知道。请知道的留言。

那么,同样是基于 WEB 访问的 WebService,使用 Delphi 来开发的时候,采用什么方式?

---------------

Delphi 可以采用的方式:

1. SOAP Header:网上能搜索到的方式,都讲使用 Soap Header,需要自己定义一个继承自 TSoapHeader 的类,用来存放用户登录后从服务器端取得的 Session 值或者干脆就是客户端自己的用户名和密码,总之就是通过这个 Soap Header 传递参数给服务器端,服务器端代码读这个 Soap Header 获得参数可以知道客户端是谁。

2. Cookie:这个是我自己摸索出来的,本来挺好用的一个功能,但在新版 Delphi 底下,稍微麻烦一点。

3. URL 参数。很简单,也很好用。但网上搜索 Delphi WebService Soap 等,没有相关信息。可能是太过简单?

----------------------------

基于 Soap Header 的方式,最大的问题是,客户端每次调用服务器端的方法时,都要事先调用一次发送 Soap header 的方法。也就是代码的量增加了。就算是客户端通过 TSoapConnection 去打开一个 TClientDataSet 或者提交一个 TClientDataSet 都需要先执行一下发送 Soap Header 的代码。

使用 Cookie,因为 Cookie 对客户端来说是透明的,客户端完全无需处理。只要服务器端为客户端设置了 Cookie,以后每次客户端的访问,服务器端都可以读到这个 Cookie,通过 Cookie 可以知道这个客户端是谁。非常好的办法。我之前也写过一篇博客介绍怎么使用 Cookie:Delphi 的 WebService 的 Cookie 操作

使用 Cookie 的问题:

1. 为了方便操作,在客户端,通常我会放一个 THTTPRIO 和一个 TSoapConnection,使用 THttpRio 来调用服务器端的接口方法,通过 TSoapConnection 来使用基于 MIDAS 的三层数据库架构,无需写代码就能获得数据,提交数据,减少代码工作量。

2. 在上述情形下,使用 HttpRio 去调用服务器端的登录方法,服务器端在该方法里面检查客户端提交的用户名密码等参数,登录成功后,服务器端为客户端创建一个 Cookie 记录客户端的参数。以后,不管是 HttpRio 调用服务器端接口,还是 ClientDataSet 通过 SoapConnection 去读写数据库,服务器端都能读到之前为客户端创建的 Cookie,根据其中参数,知道客户端是谁。

3. 但是,新版的 Delphi ,使用 HttpRIO1 去调用服务器端方法由服务器端创建的 Cookie,只能是这个 HttpRio1 再次调用服务器端其它方法时,服务器端才能读到 Cookie;如果使用 HttpRIO2 或者 SoapConnection1 去操作服务器端,服务器端无法读到之前创建的 Cookie。也就是说,新版 Delphi 的客户端里面的多个 HttpRio 和 SoapConnection 实例,没有共享服务器端创建的 Cookie。

3.1. 解决上述问题的方法稍微麻烦点,那就是即便是调用服务器端的方法,也使用 SoapConnection1.RIO 来调用,而不是另外拿一个 HttpRio1 来调用。但是,要让 SoapConnection1.RIO 去调用服务器端的接口方法,就需要:

  SoapConnection1.Connected := False;
  SoapConnection1.SOAPServerIID := '{A4E625D6-1BC4-4335-BEF4-669113CA65B4}';  //这个 ID 是你用 Delphi 写的 WebService 服务器端的接口的 IID
  SoapConnection1.Connected := True; //设计期关掉连接。运行期设置这个为 True

关于上述方法,Delphi 的官方帮助是这样说的:

If the server includes more than one remote data module, you must indicate the target data module's interface (an IAppServerSOAP descendant) so that TSOAPConnection can identify the remote data module you want to use. There are two ways to do this:

  • Set the SOAPServerIID property to indicate the interface of the target remote data module. This method works for any server that implements an IAppServerSOAP descendant. SOAPServerIID identifies the target interface by its GUID. At runtime, you can use the interface name, and the compiler automatically extracts the GUID. However, at design time, in the Object Inspector, you must specify the GUID string.
  • If the server is written using the Delphi language, you can simply include the name of the SOAP data module's interface following a slash at the end of the path portion of the URL. This lets you specify the interface by name rather than GUID, but is only available when both client and server are written in Delphi.

---------------------------------------------------------------

使用 URL 参数:

我们使用 HttpRio1 或者 SoapConnection1去连接服务器端,如果服务器端是用 Delphi 写的,通常只需要设置其 URL 为:

https://127.0.0.1:8080/soap

就可以和服务器端交互。

那么,当客户端调用服务器端的登录方法,服务器端可以为客户端产生一个 Session ID,比如一串数字 13579,通过这个登录函数返回给客户端,那么,客户端就可以把上述的 URL 改为:https://127.0.0.1:8080/soap?session=13579

那么,不管是 ClientDataSet1.Open 还是 ClientDataSet1.ApplyUpdates 还是使用 HttpRIO1 去调用服务器端的一个接口方法,在服务器端,都可以读到这个 session=13579;客户端只需要一行代码,也就是修改 URL,后面的所有操作都自动提交,无需其它代码了。服务器端读到这个参数的方法是:

ID := GetSOAPWebModule.Request.QueryFields.Values['session'];

解释一下:

在服务器端,GetSOAPWebModule 声明在 Soap.WebBrokerSOAP 单元,是一个全局函数,返回的是 TWebModule,只要 uses Soap.WebBrokerSOAP;就可以使用它。Request 代表的是客户端的请求,通过它可以读到客户端的 URL 参数。之前我们读客户端的 Cookie 也是使用 GetSOAPWebModule 来获得的。

实际上,在 Delphi 里面使用 WebBroker 框架写一个使用浏览器页面访问的 WEB Server 程序,也是通过这个 TWebModule 来操作对客户端的请求 Request 和要发送给客户端的内容 Response 的。

猜你喜欢

转载自blog.csdn.net/pcplayer/article/details/108563773