公司动态

Delphi WebService 使用 HTTP Header 管理 Session

📅 2026/8/15 1:41:21
Delphi WebService 使用 HTTP Header 管理 Session
背景知识使用 Delphi 的 WebBroker 框架可以开发标准的 Web Server 程序。使用 Delphi 的 SOAP 框架可以开发支持 SOAP 标准的 WebService 服务端程序。此框架建立的 WebBroker 框架之上。使用 Delphi 可以开发客户端程序去调用 WebService 服务器提供的 SOAP 函数。同时也可以去调用 WebBroker 开发的 Web Server 提供的 RESTfull 的 URL 接口。问题WebBroker 的架构下客户端和服务器端没有长连接是无状态的。也就是服务器端并不能记住客户端的上一次访问。因此需要一个 Session 机制。因为是 Web 访问可以采用 HTTP Cookie 来让客户端和服务器端之间维护一个 Session。这个方式的好处是客户端基本上不需要写代码。但是如果应用程序的代码框架比较复杂比如同一个客户端可能需要访问多个不同的 WebService 服务器端甚至同时还要访问多个 WebBroker 的 WebServer 服务器端。此时Cookie 因为不能跨域从一个 WebService 服务器端程序获得的 HTTP Cookie 无法让另外一个服务器端程序读到。如果要在多个 WebService 服务器端程序之间保持同一个 Session则需要考虑使用另外两种方式1. 在 URL 里面加上 Session 有关的参数2. 在 HTTP Header 里面加上 Session 有关的参数使用 HTTP Header 来维护 Session 的代码实现客户端客户端如果使用 HTTRIO 去调用服务器端的函数设计期选中 HTTPRIO1属性面板里面事件标签页HTTPWebNode 下拉开找到OnBeforePost事件双击产生事件方法代码uses Soap.SOAPHTTPTrans; procedure TForm2.HTTPRIO1HTTPWebNode1BeforePost(const HTTPReqResp: THTTPReqResp; Client: THTTPClient); begin Client.CustomHeaders[ABC] : abcdexyz; end;则每次使用这个 httpRIO1 来调用服务器端方法时都会为 HTTP 加上一个名字为 abc 的 HeaderSoapConnection 的问题是它内部的 RIO 不是一直存在的。为它设置了 URL 以后需要设置它的 Connected 属性为 True这个 RIO 才会被创建出来。因此代码可能需要显示地执行 SoapConnection1.Connected : True; 然后再判断是否有 RIO如果有则执行以下代码绑定事件SoapConnection1.RIO.HTTPWebNode.OnBeforePost : TForm2.HTTPRIO1HTTPWebNode1BeforePost;这样绑定后SoapConnection 和 HTTPRIO 应该就能共享同一个 HTTP Header 的代码了。以下代码测试通过procedure TForm2.Button2Click(Sender: TObject); begin SoapConnection1.Connected : True; if Assigned(SoapConnection1.RIO) then begin SoapConnection1.RIO.HTTPWebNode.OnBeforePost : HTTPRIO1HTTPWebNode1BeforePost; end; ClientDataSet1.Close; ClientDataSet1.Open; end;服务器端服务器端在接口方法的实现里面直接读取 HTTP Header: 的代码uses SOAP.WebBrokerSOAP, web.HttpApp; { TtestHeader } function TtestHeader.testHeader: string; begin Result : GetSOAPWebModule.Request.GetFieldByName(abc); end;服务器端的 Soap Data Module对应客户端的 SoapConnection 当客户端的 ClientDataSet.Open 的时候服务器端的 DataSetProvider.BeforeGetRecords 事件里面写代码读 Header 是否正确代码如下uses SOAP.WebBrokerSOAP, web.HttpApp; {$R *.DFM} procedure TMyDataCreateInstance(out obj: TObject); begin obj : TMyData.Create(nil); end; function TMyData.CheckHeader: Boolean; begin Result : abcdexyz GetSOAPWebModule.Request.GetFieldByName(abc); end; procedure TMyData.DataSetProvider1BeforeGetRecords(Sender: TObject; var OwnerData: OLEVariant); begin if not CheckHeader then raise Exception.Create(身份验证没通过); end;总之服务器端不管是接口方法的实现部分还是 SoapDataModule 里面都是调用GetSOAPWebModule.Request.GetFieldByName(abc);去读取 Header 来获得客户端提交的 Session ID 或者说 Token客户端使用 IdHTTPprocedure TForm2.Button3Click(Sender: TObject); var S: string; begin IdHTTP1.Request.CustomHeaders.Values[abc] : abcde; S : IdHTTP1.Get(http://localhost:8080/MyHeader); Log(S); end;上述代码使用 TIdHTTP 作为 HTTP 客户端去请求一个 WebBroker 服务器端的 MyHeader 路径。请求之前添加了一个自定义的 CustomHeader然后WebBroker 服务器端对应的代码通过以下方法读客户端传过来的 HTTP Headerprocedure TWebModule1.WebModule1MyHeaderAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); var S: string; begin S : Request.GetFieldByName(abc); Response.Content : S; end;