Title: sending a XML-file back with a TRctHTTPserver Post by: Henk vd Boogaard on May 22, 2013, 07:18:16 PM Hello,
Using the examples 1 and 2 I managed to make a webserver that receives a XML-file. I can read the file and make an answer also in a XML-file. I madk the XML-file in a TXMLdocument. How can I send this XML-file (in the TXMLDocument) as an answer? Another question: How can I test the webserver in FireFox or in MS-explorer? Besides the URL I have also include the XML-file that must be processed. Thanks in advance, Henk van den Boogaard Title: Re: sending a XML-file back with a TRctHTTPserver Post by: D.Tkalcec (RTC) on May 22, 2013, 07:34:06 PM Sender:TRtcConnection object (sent to every RTC event) has Write and WriteEx methods, which are used for sending the content body (XML document). You will need your XML content in a String (Write) or a dynamic byte array (WriteEx) in order to send it out, so check if the class you are using (TXMLDocument?) has a method which returns your XML document in a String, or an array of bytes. One thing you obviously can't do, is send an object out.
PS. You could also write the XML document into a file and then send the file out by using the Read_File function (rtcInfo.pas unit), but ... if you are creating temporary files for sending, make sure the file does not already exist (you don't want to write to the same file over and over again if your Server has to work with more than one Client (especially if it is running with MultiThreaded=True). Best Regards, Danijel Tkalcec Title: help with XML and transition to RTC Post by: Henk vd Boogaard on September 02, 2018, 12:50:37 PM Hello,
I have an old program (windows service) that listens. I if get an notify message that a pay has made and that the information can be fetched. It then puts the information in a NexusDB database. However the old program does not function anymore due to old SSL functions. I will try to rewrite the program with the use of RTC and Streamsec2 and if possible no XMLdocument. But I have not the knowledge to finsch it. I hope someone can help me. At the end is the old program with Indy. This is so far I have got: ---------------------------------------------------- unit Work; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, rtcDataSrv, rtcSystem, rtcInfo, rtcConn, rtcHttpSrv, Vcl.StdCtrls, rtcDataCli, rtcHttpCli; type TForm10 = class(TForm) Button1: TButton; Memo1: TMemo; Label1: TLabel; Ontvangen: TLabel; Edit1: TEdit; Button2: TButton; Server: TRtcHttpServer; RtcDataProvider1: TRtcDataProvider; RtcHttpClient1: TRtcHttpClient; RtcDataRequest1: TRtcDataRequest; procedure Button1Click(Sender: TObject); procedure RtcDataProvider1CheckRequest(Sender: TRtcConnection); procedure RtcDataProvider1DataReceived(Sender: TRtcConnection); procedure Button2Click(Sender: TObject); procedure RtcDataRequest1BeginRequest(Sender: TRtcConnection); procedure RtcDataRequest1DataReceived(Sender: TRtcConnection); private { Private declarations } TransactionID_str: rtcString; SiteURL, SitePort: rtcString; DoDebug: boolean; public { Public declarations } end; var Form10: TForm10; implementation {$R *.dfm} procedure TForm10.Button1Click(Sender: TObject); begin Server.ServerPort := Edit1.Text; Server.Listen(); Button2.Enabled := true; Button1.Enabled := false; DoDebug := true; end; procedure TForm10.Button2Click(Sender: TObject); begin Server.StopListen; Button2.Enabled := false; Button1.Enabled := true;; end; procedure TForm10.RtcDataProvider1CheckRequest(Sender: TRtcConnection); begin with Sender as TRtcDataServer do Memo1.Lines.Add(Request.FileName); with Sender as TRtcDataServer do if UpperCase(Request.FileName) = '/MSP' then Accept; end; procedure TForm10.RtcDataProvider1DataReceived(Sender: TRtcConnection); begin with Sender as TRtcDataServer do begin if Request.Complete then begin if DoDebug then Memo1.Lines.Clear; TransactionID_str := Request.Query['transactionid']; if DoDebug then Memo1.Lines.Add(TransactionID_str); // now get the information RtcHttpClient1.ServerAddr := SiteURL; RtcHttpClient1.ServerPort := SitePort; with RtcDataRequest1 do begin Request.Method := 'GET'; Request.FileName := '/' + TransactionID_str; // HERE THE XML?? HOW??? //WITH REQUEST.QUERY?? //WHAT BY 'Status ua' ?? Post; // Post the request end; end; end; end; procedure TForm10.RtcDataRequest1BeginRequest(Sender: TRtcConnection); begin with TRtcDataClient(Sender) do begin // make sure our request starts with "/" if Copy(Request.FileName, 1, 1) <> '/' then Request.FileName := '/' + Request.FileName; // define the "HOST" header if Request.Host = '' then Request.Host := ServerAddr; if DoDebug then Memo1.Text := 'Requesting "' + Request.FileName + '" from "' + ServerAddr + '".'; // send request header out WriteHeader; end; end; procedure TForm10.RtcDataRequest1DataReceived(Sender: TRtcConnection); begin with TRtcDataClient(Sender) do begin if Response.Started then begin { Executed only once per request, when we start receiving the response. } // Clear the info we wrote here in our "OnBeginRequest" // HERE COMES THE XML BACK?? HOW TO HANDLE THIS? //ALSO WITH REQUEST.QUERY?? if DoDebug then begin Memo1.Clear; Memo1.Lines.Add('Status code: ' + IntToStr(Response.StatusCode)); Memo1.Lines.Add('Status text:' + Response.StatusText); Memo1.Lines.Add('ALL Headers:'); Memo1.Lines.Add(Response.HeaderText); Memo1.Lines.Add('Content Length:'); Memo1.Lines.Add(IntToStr(Response.ContentLength)); Memo1.Lines.Add('Content body:'); Memo1.Lines.Add('START >'); end; { Could be executed more than once, depending on the content size } // add content received now. Memo1.Text := Memo1.Text + Read; if Response.Done then begin { Executed only once per request, when we have just received it all. } Memo1.Lines.Add('< END'); end; end; //DoDebug end; end; end. ------------------------------------------------- ======================the old program=========================== unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs, IdBaseComponent, IdComponent, IdCustomTCPServer, IdCustomHTTPServer, IdHTTPServer, Variants, Forms, xmldom, XMLIntf, msxmldom, XMLDoc, IdAntiFreezeBase, IdAntiFreeze, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL, IdTCPConnection, IdTCPClient, IdHTTP, IdServerIOHandler, IniFiles, IdContext, IdHeaderList, ActiveX, DB, nxdb, nxsdServerEngine, nxreRemoteServerEngine, nxllComponent, nxllTransport, nxptBasePooledTransport, nxtwWinsockTransport, Xml.omnixmldom; type TService1 = class(TService) IdHTTPServer1: TIdHTTPServer; IdServerIOHandlerSSLOpenSSL1: TIdServerIOHandlerSSLOpenSSL; IdHTTP1: TIdHTTP; IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL; IdAntiFreeze1: TIdAntiFreeze; XMLDoc1: TXMLDocument; XMLDoc2: TXMLDocument; nxWinsockTransport1: TnxWinsockTransport; nxRemoteServerEngine1: TnxRemoteServerEngine; nxSession1: TnxSession; nxDatabase1: TnxDatabase; DS_algpar: TDataSource; nxT_algpar: TnxTable; DS_IDealRecords: TDataSource; nxT_IDealRecords: TnxTable; procedure ServiceStart(Sender: TService; var Started: Boolean); procedure IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); procedure IdHTTPServer1CreatePostStream(AContext: TIdContext; AHeaders: TIdHeaderList; var VPostStream: TStream); procedure ServiceStop(Sender: TService; var Stopped: Boolean); private { Private declarations } protected function DoStop: Boolean; override; function DoPause: Boolean; override; function DoContinue: Boolean; override; procedure DoInterrogate; override; procedure DoShutdown; override; public constructor Create(AOwner: TComponent); override; function GetServiceController: TServiceController; override; { Public declarations } end; var Service1: TService1; AppsPad: string; XML_Dir: string; LogAanUit: string; implementation uses CodeSiteLogging; {$R *.DFM} procedure ServiceController(CtrlCode: DWord); stdcall; begin Service1.Controller(CtrlCode); end; constructor TService1.Create(AOwner: TComponent); var Ini: TInifile; DB_naam: string; DB_Alias: string; DB_Poort: integer; ServerPoort: Word; begin inherited; CoInitializeEx(nil, 2); CodeSite.Clear; DisplayName := 'AVN Service'; // setup: AppsPad := ExtractFilePath(Application.ExeName); // BS geen JclSysInfo meer nodig... CodeSite.Send('AppsPad = ' + AppsPad); Ini := TInifile.Create(AppsPad + 'AVN_SERVICE_DB_Settings.ini'); try DB_naam := Ini.ReadString('ServerInfo', 'Server', 'NEXUSDB'); DB_Poort := Ini.ReadInteger('Transport', 'Poort', 16000); // 16000 voor gewoon 17000 voor secure DB_Alias := Ini.ReadString('Alias', 'Aliasnaam', 'AVN'); ServerPoort := Ini.ReadInteger('Com', 'ServerPoort', 22333); XML_Dir := Ini.ReadString('FILES', 'XML_Dir', 'C:\TEMP\'); LogAanUit := Ini.ReadString('LOG', 'LogAanUit', 'AAN'); CodeSite.Send('1a'); finally Ini.Free; CodeSite.Send('1b'); CodeSite.Send('DB ' + DB_naam); CodeSite.Send('Poort ' + IntToStr(DB_Poort)); CodeSite.Send('Alias ' + DB_Alias); end; Application.ProcessMessages; // setup de database connectie: // INDIEN ALS SERVICE DAN DE INI-FILE IN DE MAP WINDOWS/SYSTEM32 ZETTEN nxWinsockTransport1.Active := false; CodeSite.Send('1c'); nxWinsockTransport1.ServerName := DB_naam; CodeSite.Send('1d'); nxDatabase1.AliasPath := ''; CodeSite.Send('1e'); nxDatabase1.AliasName := DB_Alias; CodeSite.Send('1f'); nxWinsockTransport1.Port := DB_Poort; CodeSite.Send('1g'); nxWinsockTransport1.Active := true; CodeSite.Send('1h'); Application.ProcessMessages; if nxWinsockTransport1.Connected then begin CodeSite.Send('Database geopend'); nxRemoteServerEngine1.Active := true; nxSession1.Active := true; nxDatabase1.Active := true; CodeSite.Send('Bestanden geopend'); // einde setup nxT_algpar.Open; nxT_IDealRecords.Open; Application.ProcessMessages; IdServerIOHandlerSSLOpenSSL1.SSLOptions.CertFile := nxT_algpar.FieldByName('IDeal_SSL_Cert_file').AsString; // '83.161.204.179.cer'; IdServerIOHandlerSSLOpenSSL1.SSLOptions.KeyFile := nxT_algpar.FieldByName('IDeal_SSL_Cert_key').AsString; // '83.161.204.179.key'; IdServerIOHandlerSSLOpenSSL1.SSLOptions.Method := sslvTLSv1_2; IdServerIOHandlerSSLOpenSSL1.SSLOptions.Mode := sslmBoth; IdServerIOHandlerSSLOpenSSL1.SSLOptions.VerifyMode := []; IdServerIOHandlerSSLOpenSSL1.SSLOptions.VerifyDepth := 0; IdSSLIOHandlerSocketOpenSSL1.SSLOptions.CertFile := nxT_algpar.FieldByName('IDeal_SSL_Cert_file').AsString; // '83.161.204.179.cer'; IdSSLIOHandlerSocketOpenSSL1.SSLOptions.KeyFile := nxT_algpar.FieldByName('IDeal_SSL_Cert_key').AsString; // '83.161.204.179.key'; IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Method := sslvTLSv1_2; IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Mode := sslmBoth; IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyMode := []; IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyDepth := 0; Application.ProcessMessages; end else CodeSite.Send('fout geen connectie'); IdHTTPServer1.DefaultPort := ServerPoort; CodeSite.Send(IntToStr(ServerPoort)); // IdHTTPServer1.Active := true; -- BS: Dat doen we pas in de ServiceStart zelf! OnStart := ServiceStart; end; function TService1.DoContinue: Boolean; begin CodeSite.Send('Service Continue'); try IdHTTPServer1.Active := true; except on E: Exception do CodeSite.SendException(E); end; Result := inherited; end; procedure TService1.DoInterrogate; begin inherited; end; function TService1.DoPause: Boolean; begin CodeSite.Send('Service Pause'); try IdHTTPServer1.Active := false; except on E: Exception do CodeSite.SendException(E); end; Result := inherited; end; procedure TService1.DoShutdown; begin CodeSite.Send('Service Shutdown'); try IdHTTPServer1.Active := false; except on E: Exception do CodeSite.SendException(E); end; inherited; end; function TService1.DoStop: Boolean; begin CodeSite.Send('Service Stop'); try IdHTTPServer1.Active := false; except on E: Exception do CodeSite.SendException(E); end; Result := inherited; end; function TService1.GetServiceController: TServiceController; begin Result := ServiceController; end; procedure TService1.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); var HtmlResult: String; Request: String; IDealRecNr: integer; Exportfilenaam: string; iChild, iNodeEwallet, iNodeStatus, iNode_Merchant, iNodeTransaction: IXMLNode; Resulttekst, Statustekst: string; Strm: TStringStream; begin CodeSite.EnterMethod('command get'); CoInitialize(nil); Request := Uppercase(ARequestInfo.Document); HtmlResult := ARequestInfo.Params.Values['transactionid']; if HtmlResult <> '' then begin try CodeSite.Send('IDealRecNr = ' + HtmlResult); CodeSite.Send('record ontvangen'); IDealRecNr := StrToInt(HtmlResult); // nu in database Huurovereenkomst controleren - indien OK dan VerstuurHTML: // maak XML-file: { if not DirectoryExists(XML_Dir) then CreateDir(XML_Dir); Exportfilenaam := XML_Dir + 'IDeal_' + IntToStr(IDealRecNr) + '.XML'; dit alleen voor testen anders de volgende regel: } Exportfilenaam := 'IDeal_' + IntToStr(IDealRecNr) + '.XML'; CodeSite.Send('Exportfilename = ' + Exportfilenaam); XMLDoc1.Active := false; XMLDoc1.XML.Text := ''; XMLDoc1.Active := true; XMLDoc1.Encoding := 'UTF-8'; XMLDoc1.AddChild('Pricat', 'http://www.ean.nl'); XMLDoc1.DocumentElement := XMLDoc1.CreateNode('status'); XMLDoc1.DocumentElement.Attributes['ua'] := 'Mozilla/3.0 (compatible; Indy Library)'; // merchant iNode_Merchant := XMLDoc1.DocumentElement.AddChild('merchant'); iChild := iNode_Merchant.AddChild('account'); iChild.Text := nxT_algpar.FieldByName('IDeal_account').AsString; iChild := iNode_Merchant.AddChild('site_id'); iChild.Text := nxT_algpar.FieldByName('IDeal_site_id').AsString; iChild := iNode_Merchant.AddChild('site_secure_code'); iChild.Text := nxT_algpar.FieldByName('IDeal_site_secure_id').AsString; // transaction iNodeTransaction := XMLDoc1.DocumentElement.AddChild('transaction'); iChild := iNodeTransaction.AddChild('id'); iChild.Text := IntToStr(IDealRecNr); // bestand maken: XMLDoc1.SaveToFile(Exportfilenaam); CodeSite.Send('XMLDoc1 saved to file'); // versturen: Strm := TStringStream.Create; try // alleen voor debug anders regel hieronder Strm.LoadFromFile(Exportfilenaam); XMLDoc1.SaveToStream(Strm); IdHTTP1.Request.ContentType := 'text/xml'; XMLDoc2.LoadFromXML(IdHTTP1.Post(nxT_algpar.FieldByName('IDeal_Site_URL').AsString, Strm)); Resulttekst := XMLDoc2.DocumentElement.Attributes['result']; iNodeEwallet := XMLDoc2.DocumentElement.ChildNodes.FindNode('ewallet'); iNodeStatus := iNodeEwallet.ChildNodes.FindNode('status'); if Assigned(iNodeStatus) then Statustekst := iNodeStatus.Text; CodeSite.Send('Status = ' + Statustekst); // database bijwerken: if nxT_IDealRecords.FindKey([IDealRecNr]) then //hier volgend in v.1.1.0 gewijzigd omdat de status niet juist werd vertaald: begin if Uppercase(Resulttekst) = 'OK' then begin nxT_IDealRecords.Edit; nxT_IDealRecords.FieldByName('StatusOntvangen').AsBoolean := true; nxT_IDealRecords.FieldByName('Status').AsString := Statustekst; if Uppercase(Statustekst) = 'COMPLETED' then nxT_IDealRecords.FieldByName('BetalingOK').AsBoolean := true; nxT_IDealRecords.Post; end else begin nxT_IDealRecords.Edit; nxT_IDealRecords.FieldByName('StatusOntvangen').AsBoolean := false; nxT_IDealRecords.FieldByName('Status').AsString := Statustekst; nxT_IDealRecords.FieldByName('Foutkode').AsString := Resulttekst; nxT_IDealRecords.Post; end; end else CodeSite.Send('Kan IDealrecord niet vinden'); // bewaren als laatste zodat eerst de database wordt bijgewerkt // alleen voor debug: XMLDoc2.SaveToFile(XML_Dir + 'result' + IntToStr(IDealRecNr) + '.xml'); finally Strm.Free; end; except on E: Exception do CodeSite.SendException('fout(en) bij communicatie', E); end; end; CodeSite.ExitMethod('command get'); end; procedure TService1.IdHTTPServer1CreatePostStream(AContext: TIdContext; AHeaders: TIdHeaderList; var VPostStream: TStream); begin VPostStream := TMemoryStream.Create; end; procedure TService1.ServiceStart(Sender: TService; var Started: Boolean); begin CodeSite.Send('Service Start'); CoInitialize(nil); try IdHTTPServer1.Active := true; Started := true; except on E: Exception do CodeSite.SendException(E); end; end; procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean); begin CoUninitialize end; end. =================================== Greetings Henk van den Boogaard Title: Re: help with XML and transition to RTC Post by: Henk vd Boogaard on September 03, 2018, 12:28:13 PM Hello Danijel,
After I get a TransactionID from the client I must sent a XML to get back an XML with the information I need. I was thinking to get that with a RtcHttpClient. Henk Title: Re: help with XML and transition to RTC Post by: Henk vd Boogaard on September 03, 2018, 12:35:55 PM I was thinking that a RtcDataRequest needs a RtcHttpClient
Title: Re: help with XML and transition to RTC Post by: D.Tkalcec (RTC) on September 03, 2018, 01:08:20 PM If your Server can remain single-threaded (RtcHttpServer.MultiThreaded=FALSE), you can use a single RtcHttpClient component to send out requests to external Servers and get the data, but ... please keep in mind that doing this also means that your Server is ONLY capable of handling one request from your Clients at a time. If another Client sends a request to your Server while your Server is fetching data from the external Server, it will have to wait until the Server exits the "OnDataReceived" event where it's been waiting for a response from the external Server.
So far, your code looks OK. Now, you need to implement the "OnDataReceived" event for your "RtcDataReuqest" component, because that's where you will get the response from your external Server after you post the request with the "RtcDataRequest" and "RtcHttpClient1" component. Since your Server is single-threaded and you are only using one RtcHttpClient component, you can also use one private variable to store the response which you get from the external Server. You will also need to make your client connection BLOCKING for this to work, which can either be done by setting the "useProxy", "useWinHttp" or "useBlocking" property to TRUE on your RtcHttpClient1 component (not sure if you did that already, since I don't see your Form). If you do that, you don't have to worry about RtcHttpClient1 or RtcDataRequest component events triggered asynchronously while you are waiting for a response from the Client. Since you are using a single RtcHttpClient component and sending requests to a custom Server each time, also make sure to call Disconnect after you read the response, so you won't end up sending all the future requests to the same external Server. Let me know if you need any more help. Title: Re: help with XML and transition to RTC Post by: Henk vd Boogaard on September 03, 2018, 01:30:29 PM I have not made thinks clear to you so I try again.
I have a website where a customer can login to extent the hire of a car. After he as filled in the necessary information he get redirected to MultisafePay (a firm that handles a payment by Ideal. When the payment has made MultisafePay sent out a message (notification) to this program that there has been a mutation on the hire (a payment has been made or it did not succeed). This program gets the notification and must the fetch the payment information by MultisafePay and process the information in the database. My big question is how can process the information I get in a XML and how can I sent information to ask to get the payment information back in a XML. In sort how can I handle the I get XML’s and I sent without using XMLDocument. And also one tag of the outgoing XML is Attributes['ua'] (XMLDoc1.DocumentElement.Attributes['ua'] := 'Mozilla/3.0 (compatible; Indy Library)';) What must I put in when using RTC? Thanks for helping me. Title: Re: help with XML and transition to RTC Post by: D.Tkalcec (RTC) on September 03, 2018, 01:35:48 PM Since RTC does NOT include components for XML processing (you simply send out raw HTTP/S data and get raw HTTP/S data back), even if you are using RTC for communication, you still need something else to handle the XML. Is there a problem with the XML code you've used with Indy, or why are you looking for a solution which would NOT use that part of your old code?
Title: Re: help with XML and transition to RTC Post by: Henk vd Boogaard on September 03, 2018, 02:02:14 PM I thought RTC could handle XML’s, my mistake and that was easier.
Then I go use the old code again. Still I have two questions: 1. How van I sent / receice the XML’s I made / got. 2. What must I put in the Attributes['ua'] tag Henk Title: Re: help with XML and transition to RTC Post by: D.Tkalcec (RTC) on September 03, 2018, 02:14:47 PM 1. Using RtcHttpClient and RtcDataRequest components, you can send any kind of HTTP request and receive a HTTP response. Check this topic from the RTC Classroom for a step-by-step guide on using the RTC SDK to send a request and receive a response from a Server:
https://rtc.teppi.net/realthinclient-sdk-demo-6-client-to-get-a-file-from-any-web-server/ 2. I'm not sure what you mean, but looking at your code, it seems like the Attributes['ua'] from your example is just a name of the Client and only has informative functionality for the receiving Server, so ... you can either set the same as you've already used with Indy, or replace the "Indy Library" part of that text with "RTC" and see what happens ;) Title: Re: help with XML and transition to RTC Post by: Henk vd Boogaard on September 03, 2018, 02:17:31 PM I will give it a try and give you the results.
Thanks, Henk Title: Re: help with XML and transition to RTC Post by: D.Tkalcec (RTC) on September 03, 2018, 02:20:19 PM OK. Just keep in mind that your code needs to use "WaitForCompletion" after calling "Post" and "Connect" on the "RtcHttpClient" component to wait for the response (emulate blocking behavior), and ... you need to set "useProxy", "useWinHttp" or "useBlocking" property to TRUE on the "RtcHttpClient" component to make the communication blocking. Otherwise, your calls to "Post" and "Connect" will return immediately (before you get any data from the Server) and your request call would then be handled asynchronously (afterwards).
Also, if you have to communicate with different Servers for each request, make sure to call "Disconnect" after you get the data, to avoid problems with changing Servers (ServerAddr : ServerPort). On the other hand, if the Server is always the same and you expect to be sending more requests there, there's no need for a "Disconnect". Title: Re: sending a XML-file back with a TRctHTTPserver Post by: Henk vd Boogaard on September 04, 2018, 03:56:05 PM Hi Danijel,
Will you take a short look if my code should work like this? Also, the RtcDataRequest should be with HTTPS. I try to use StreamSec2. Is there an example how to do that? here my code so far: unit U_Work; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, rtcDataSrv, rtcSystem, rtcInfo, rtcConn, rtcHttpSrv, Vcl.StdCtrls, rtcDataCli, rtcHttpCli, Data.DB, Vcl.Grids, Vcl.DBGrids, Xml.xmldom, Xml.XMLIntf, Xml.omnixmldom, Xml.XMLDoc, Winapi.ActiveX; type TWork = class(TForm) Memo1: TMemo; Ontvangen: TLabel; Server: TRtcHttpServer; RtcDataProvider1: TRtcDataProvider; RtcHttpClient1: TRtcHttpClient; RtcDataRequest1: TRtcDataRequest; DBGrid1: TDBGrid; label2: TLabel; XMLDoc1: TXMLDocument; XMLDoc2: TXMLDocument; procedure RtcDataProvider1CheckRequest(Sender: TRtcConnection); procedure RtcDataProvider1DataReceived(Sender: TRtcConnection); procedure RtcDataRequest1BeginRequest(Sender: TRtcConnection); procedure RtcDataRequest1DataReceived(Sender: TRtcConnection); private { Private declarations } TransactionID_str: rtcString; StatusText: string; IDealRecNr: integer; StatusReceived: boolean; Exportfilenaam: string; iChild, iNodeEwallet, iNodeStatus, iNode_Merchant, iNodeTransaction: IXMLNode; // Resulttekst, Statustekst: string; Strm: TStringStream; XML_string : rtcString; XML_response : rtcString; procedure FillRecords; public { Public declarations } DoDebug: boolean; end; var Work: TWork; implementation {$R *.dfm} uses U_DM1; procedure TWork.FillRecords; begin try if DM1.nxT_IDealRecords.FindKey([IDealRecNr]) then begin if Uppercase(StatusText) = 'OK' then begin DM1.nxT_IDealRecords.Edit; DM1.nxT_IDealRecords.FieldByName('StatusOntvangen').AsBoolean := true; DM1.nxT_IDealRecords.FieldByName('Status').AsString := StatusText; if Uppercase(StatusText) = 'COMPLETED' then DM1.nxT_IDealRecords.FieldByName('BetalingOK').AsBoolean := true; DM1.nxT_IDealRecords.Post; end else begin DM1.nxT_IDealRecords.Edit; DM1.nxT_IDealRecords.FieldByName('StatusOntvangen').AsBoolean := false; DM1.nxT_IDealRecords.FieldByName('Status').AsString := StatusText; DM1.nxT_IDealRecords.FieldByName('Foutkode').AsString := StatusText; DM1.nxT_IDealRecords.Post; end; end else if DoDebug then Memo1.Lines.Add('Kan IDealrecNr niet vinden'); except on E: Exception do if DoDebug then Memo1.Lines.Add('Fout bij bewerken status in IDealrec. Error: ' + E.Message); end; end; procedure TWork.RtcDataProvider1CheckRequest(Sender: TRtcConnection); begin with Sender as TRtcDataServer do Memo1.Lines.Add(Request.FileName); with Sender as TRtcDataServer do if Uppercase(Request.FileName) = '/MSP' then Accept; end; procedure TWork.RtcDataProvider1DataReceived(Sender: TRtcConnection); begin with Sender as TRtcDataServer do begin if Request.Complete then begin if DoDebug then Memo1.Lines.Clear; TransactionID_str := Request.Query['transactionid']; if DoDebug then Memo1.Lines.Add(TransactionID_str); try IDealRecNr := StrToInt(TransactionID_str); except on E: Exception do if DoDebug then Memo1.Lines.Add('Fout bij vertalen recordnummer van parameter : ' + E.Message); end; with RtcDataRequest1 do begin Request.Method := 'GET'; Request.FileName := '/' + TransactionID_str; CoInitialize(nil); // nu in database Huurovereenkomst controleren - indien OK dan VerstuurHTML: // maak XML-file: Exportfilenaam := 'IDeal_' + IntToStr(IDealRecNr) + '.XML'; XMLDoc1.Active := false; XMLDoc1.Xml.Text := ''; XMLDoc1.Active := true; XMLDoc1.Encoding := 'UTF-8'; XMLDoc1.AddChild('Pricat', 'http://www.ean.nl'); XMLDoc1.DocumentElement := XMLDoc1.CreateNode('status'); XMLDoc1.DocumentElement.Attributes['ua'] := 'RTC Library)'; // merchant iNode_Merchant := XMLDoc1.DocumentElement.AddChild('merchant'); iChild := iNode_Merchant.AddChild('account'); iChild.Text := DM1.nxT_algpar.FieldByName('IDeal_account').AsString; iChild := iNode_Merchant.AddChild('site_id'); iChild.Text := DM1.nxT_algpar.FieldByName('IDeal_site_id').AsString; iChild := iNode_Merchant.AddChild('site_secure_code'); iChild.Text := DM1.nxT_algpar.FieldByName('IDeal_site_secure_id').AsString; // transaction iNodeTransaction := XMLDoc1.DocumentElement.AddChild('transaction'); iChild := iNodeTransaction.AddChild('id'); iChild.Text := IntToStr(IDealRecNr); // bestand maken: if DoDebug then XMLDoc1.SaveToFile(DM1.XML_Dir + Exportfilenaam); XMLDoc1.SaveToXML(XML_string); // now get the information RtcHttpClient1.ServerAddr := DM1.SiteURL; RtcHttpClient1.ServerPort := IntToStr(DM1.SitePort); RtcHttpClient1.Write(XML_string); Post; // Post the request WaitForCompletion; XMLDoc1.Active := false; XMLDoc2.LoadFromXML(RtcHttpClient1.Response); // Response is an XML file //NOW GET THE INFORMATION FROM xmldOC2 CoUninitialize; end; end; end; end; procedure TWork.RtcDataRequest1BeginRequest(Sender: TRtcConnection); begin with TRtcDataClient(Sender) do begin // make sure our request starts with "/" if Copy(Request.FileName, 1, 1) <> '/' then Request.FileName := '/' + Request.FileName; // define the "HOST" header if Request.Host = '' then Request.Host := ServerAddr; if DoDebug then Memo1.Text := 'Requeststring "' + Request.FileName + '" from "' + ServerAddr + '".'; // send request header out WriteHeader; end; end; procedure TWork.RtcDataRequest1DataReceived(Sender: TRtcConnection); begin with TRtcDataClient(Sender) do begin if Response.Started then begin { Executed only once per request, when we start receiving the response. } // Clear the info we wrote here in our "OnBeginRequest" // HERE COMES THE XML BACK?? HOW TO HANDLE THIS? // ALSO WITH REQUEST.QUERY?? if DoDebug then begin Memo1.Clear; Memo1.Lines.Add('Status code: ' + IntToStr(Response.StatusCode)); Memo1.Lines.Add('Status text:' + Response.StatusText); Memo1.Lines.Add('ALL Headers:'); Memo1.Lines.Add(Response.HeaderText); Memo1.Lines.Add('Content Length:'); Memo1.Lines.Add(IntToStr(Response.ContentLength)); Memo1.Lines.Add('Content body:'); Memo1.Lines.Add('START >'); end; { Could be executed more than once, depending on the content size } // add content received now. Memo1.Text := Memo1.Text + Read; if Response.Done then begin { Executed only once per request, when we have just received it all. } Memo1.Lines.Add('< END'); end; end; // DoDebug end; end; end. The DMF: object Work: TWork Left = 0 Top = 0 Caption = 'AVN_MSP_SERVER' ClientHeight = 671 ClientWidth = 947 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13 object Ontvangen: TLabel Left = 20 Top = 8 Width = 72 Height = 13 Caption = 'Ontvangen/log' end object label2: TLabel Left = 20 Top = 352 Width = 120 Height = 13 Caption = 'test of database actief is' end object Memo1: TMemo Left = 146 Top = 8 Width = 671 Height = 297 ScrollBars = ssBoth TabOrder = 0 end object DBGrid1: TDBGrid Left = 146 Top = 352 Width = 671 Height = 233 DataSource = DM1.DS_IDealRecords TabOrder = 1 TitleFont.Charset = DEFAULT_CHARSET TitleFont.Color = clWindowText TitleFont.Height = -11 TitleFont.Name = 'Tahoma' TitleFont.Style = [] end object Server: TRtcHttpServer RestartOn.ListenLost = True RestartOn.ListenError = True RestartOn.Wait = 10 Blocking = True Left = 848 Top = 144 end object RtcDataProvider1: TRtcDataProvider Server = Server OnCheckRequest = RtcDataProvider1CheckRequest OnDataReceived = RtcDataProvider1DataReceived Left = 848 Top = 200 end object RtcHttpClient1: TRtcHttpClient ReconnectOn.ConnectLost = True ReconnectOn.Wait = 5 Blocking = True Left = 848 Top = 264 end object RtcDataRequest1: TRtcDataRequest AutoSyncEvents = True Client = RtcHttpClient1 AutoRepost = 2 OnBeginRequest = RtcDataRequest1BeginRequest OnDataReceived = RtcDataRequest1DataReceived Left = 848 Top = 328 end object XMLDoc1: TXMLDocument Left = 848 Top = 400 DOMVendorDesc = 'Omni XML' end object XMLDoc2: TXMLDocument Left = 848 Top = 464 DOMVendorDesc = 'Omni XML' end end Title: Re: sending a XML-file back with a TRctHTTPserver Post by: D.Tkalcec (RTC) on September 05, 2018, 09:05:28 AM I've just realized that there are two topics from you related to the same question, so I've merged the two topics together now to avoid confusion.
Title: Re: sending a XML-file back with a TRctHTTPserver Post by: D.Tkalcec (RTC) on September 05, 2018, 09:57:40 AM I've spotted a few problems with your latest code (related to wrong use of the RTC SDK)...
1. The Write method of the TRtcHttpClient component may ONLY be used from inside an event triggered by that component. In other words, your "RtcHttpClient1.Write(XML_string)" line inside the "RtcDataProvider1DataReceived" event is illegal and would most likely cause you problems. However, if you already have the complete request headers and the request content body ready for sending and you are confident that the content is small enough to fit into memory without causing you memory problems, then you can use the "PostMethod" method on the "RtcDataRequest" component to have request headers and content body stored for you in temporary buffers and sent out after the "OnBeginRequest" event. This also works if you skip implementing the "OnBeginRequest", provided you also prepare all required headers before calling "PostMethod". In other words, this line replaces your "RtcHttpClient1.Write(XML_string);" and "Post" calls in the "RtcDataProvider1DataReceived", but it is entirely legal and does what you intended (provided you are using RTC SDK v9) ... RtcDataRequest1.PostMethod('',XML_string); 2. You've set Request.Method='GET', but since you are posting a request with content body, I have a feeling that this is actually a "POST" request, so you might need to change that to Request.Method='POST' in your code, or simply remove that Request.Method assignment line from your code and use this line instead of the one I've recommended above (point 1) ... RtcDataRequest1.PostMethod('POST',XML_string); 3. You are storing the response you get from the external Server inside the Memo.Text, which is fine for debugging but bad for production. You should change this to store the response content in a variable, so it doesn't get messed up with Memo component formatting. In other words, instead of doing this inside your "RtcDataRequest1DataReceived" event -> // add content received now. Memo1.Text := Memo1.Text + Read; You should declare a variable like "RecvContent:RtcString", then you can do this inside the "RtcDataRequest1DataReceived" event -> // add content received now. RecvContent:=Sender.Read; Memo1.Text := RecvContent; This makes your response content body accessible inside the "RtcDataProvider1DataReceived" event, after the "WaitForCompletion" call, so you can do this to access the XML string received as part of the response content body -> XMLDoc2.LoadFromXML(RecvContent); // Response is an XML file Let me know if you have any other question. Title: Re: sending a XML-file back with a TRctHTTPserver Post by: Henk vd Boogaard on September 05, 2018, 11:01:39 AM First, I indeed posted my reply in the wrong place.
I was looking there for you answer for my xml questions, but that program you advised me to work with a rtcFunction and that program works fine. However now I must use xml and I am struggling again. I apricate very much that you help so good. =========================== I altered the code as you suggested and made also the last additions. I post here the last bit. Only I get an error on: RtcDataRequest1.PostMethod('POST', XML_string); // <--- GIVES A COMPILE ERROR 'THERE IS NO OVERLOADED VERSION OF THIS' Also can you point me to a good example to implement StreamSec2 for the RtcDataRequest for that must be in HTTPS. Thanks Henk ==================== procedure TWork.RtcDataProvider1DataReceived(Sender: TRtcConnection); var RecvContent, ResultText, StatusText: rtcString; begin with Sender as TRtcDataServer do begin if Request.Complete then begin RecvContent := Sender.Read; TransactionID_str := Request.Query['transactionid']; if DoDebug then Memo1.Text := Memo1.Text + READ; Memo1.Lines.Add('ID=' + TransactionID_str); try IDealRecNr := StrToInt(TransactionID_str); except on E: Exception do if DoDebug then Memo1.Lines.Add('Fout bij vertalen recordnummer van parameter : ' + E.Message); end; with RtcDataRequest1 do begin Request.FileName := '/' + TransactionID_str; CoInitialize(nil); // nu in database Huurovereenkomst controleren - indien OK dan VerstuurHTML: // maak XML-file: Exportfilenaam := 'IDeal_' + IntToStr(IDealRecNr) + '.XML'; XMLDoc1.Active := false; XMLDoc1.Xml.Text := ''; XMLDoc1.Active := true; XMLDoc1.Encoding := 'UTF-8'; XMLDoc1.AddChild('Pricat', 'http://www.ean.nl'); XMLDoc1.DocumentElement := XMLDoc1.CreateNode('status'); XMLDoc1.DocumentElement.Attributes['ua'] := 'RTC Library)'; // merchant iNode_Merchant := XMLDoc1.DocumentElement.AddChild('merchant'); iChild := iNode_Merchant.AddChild('account'); iChild.Text := DM1.nxT_algpar.FieldByName('IDeal_account').AsString; iChild := iNode_Merchant.AddChild('site_id'); iChild.Text := DM1.nxT_algpar.FieldByName('IDeal_site_id').AsString; iChild := iNode_Merchant.AddChild('site_secure_code'); iChild.Text := DM1.nxT_algpar.FieldByName('IDeal_site_secure_id').AsString; // transaction iNodeTransaction := XMLDoc1.DocumentElement.AddChild('transaction'); iChild := iNodeTransaction.AddChild('id'); iChild.Text := IntToStr(IDealRecNr); // bestand maken: if DoDebug then XMLDoc1.SaveToFile(DM1.XML_Dir + Exportfilenaam); XMLDoc1.SaveToXML(XML_string); // now get the information RtcHttpClient1.ServerAddr := DM1.SiteURL; RtcHttpClient1.ServerPort := IntToStr(DM1.SitePort); // Post the request with 'POST' : RtcDataRequest1.PostMethod('POST', XML_string); // <--- GIVES A COMPILE ERROR 'THERE IS NO OVERLOADED VERSION OF THIS' WaitForCompletion; XMLDoc1.Active := false; XMLDoc2.Active := false; XMLDoc2.LoadFromXML(RecvContent); // Response is an XML file XMLDoc2.Active := true; // now get the information from XMLdoc2: ResultText := XMLDoc2.DocumentElement.Attributes['result']; iNodeEwallet := XMLDoc2.DocumentElement.ChildNodes.FindNode('ewallet'); iNodeStatus := iNodeEwallet.ChildNodes.FindNode('status'); if Assigned(iNodeStatus) then StatusText := iNodeStatus.Text; // database bijwerken: if DM1.nxT_IDealRecords.FindKey([IDealRecNr]) then begin if Uppercase(ResultText) = 'OK' then begin DM1.nxT_IDealRecords.Edit; DM1.nxT_IDealRecords.FieldByName('StatusOntvangen').AsBoolean := true; DM1.nxT_IDealRecords.FieldByName('Status').AsString := StatusText; if Uppercase(StatusText) = 'COMPLETED' then DM1.nxT_IDealRecords.FieldByName('BetalingOK').AsBoolean := true; DM1.nxT_IDealRecords.Post; end else begin DM1.nxT_IDealRecords.Edit; DM1.nxT_IDealRecords.FieldByName('StatusOntvangen').AsBoolean := false; DM1.nxT_IDealRecords.FieldByName('Status').AsString := StatusText; DM1.nxT_IDealRecords.FieldByName('Foutkode').AsString := ResultText; DM1.nxT_IDealRecords.Post; end; end else if DoDebug then Memo1.Lines.Add('Kan IDealrecord niet vinden'); // bewaren als laatste zodat eerst de database wordt bijgewerkt if DoDebug then XMLDoc2.SaveToFile(DM1.XML_Dir + DateTimeToStr(now) + 'result' + IntToStr(IDealRecNr) + '.xml'); CoUninitialize; end; end; end; end; Title: Re: sending a XML-file back with a TRctHTTPserver Post by: D.Tkalcec (RTC) on September 05, 2018, 11:18:30 AM Oops :-[ Sorry, my mistake. You need the "Write" method on the "RtcDataRequest" component (not on the "RtcHttpClient" component) to prepare the request content body which you want to send, then you can use "PostMethod" to post your request without the need to implement the "OnBeginRequest" and "OnDataSent" events for sending the request headers and/or content out.
In other words, you need ... RtcDataRequest1.Write(XML_string); RtcDataRequest1.PostMethod('POST'); As for your StreamSec question, please contact the StreamSec component vendor with ANY questions you may have about using their components. Title: Re: sending a XML-file back with a TRctHTTPserver Post by: D.Tkalcec (RTC) on September 05, 2018, 11:30:23 AM I also see that you've misunderstood my intention with the "RecvContent" variable. It has to be a global variable or a variable of your main class (not local to any event) and you need to use the "Sender.Read" method inside the "OnDataReiceved" event of your RtcDataRequest component if you want the response from your external Server (received by your RtcDataRequest component, which is attached to your RtcHttpClient companent).
So ... what you did now by calling "RecvContent:=Read" inside your "RtcDataProviderDataReceived" event gives you the request content which your Server has received from your inbound Client and NOT the response content body sent to you from your external Server. On one side, you have your Server communicating with Clients through "RtcHttpServer" and the linked "RtcDataProvider" component (using events attached to these components), while on the other side you have the same process communicating with another external Server by using the "RtcHttpClient" and "RtcDataRequest" (using events on these components). These are two completely separate communication channels, each with its own request and response data, so need to watch out and sure you do NOT mix them up! It's a HUGE difference where and on which components you call "Read" and "Write"!!! Title: Re: sending a XML-file back with a TRctHTTPserver Post by: Henk vd Boogaard on September 10, 2018, 11:33:17 AM Hi Danijel
Are you sure that sending with: RtcDataRequest1.Write(XML_string); RtcDataRequest1.PostMethod('POST'); is working within the procedure RtcDataProvider1DataReceived(Sender: TRtcConnection);? It seems there is nothing sent out. Can I email you a link so you can download my program for testing? Henk van den Boogaard Title: Re: sending a XML-file back with a TRctHTTPserver Post by: D.Tkalcec (RTC) on September 10, 2018, 01:03:42 PM Remove the "WriteHeader" call from your "RtcDataRequest1BeginRequest" event. That call makes it impossible to send out the content you've prepared with the "RtcDataRequest1.Write(XML_string)".
Why? Because you are NOT setting the "Request.ContentLength", which HAS TO BE SET manually before calling "WriteHeader". If you just call "WriteHeader" but have NOT set the "Request.ContentLength" before, you will end up sending a request with NO CONTENT BODY. Title: Re: sending a XML-file back with a TRctHTTPserver Post by: Henk vd Boogaard on September 10, 2018, 04:15:42 PM Hi Danijel,
How can I see/get the complete message before it is being POST? So I can check it of it is what MSP want. Henk Title: Re: sending a XML-file back with a TRctHTTPserver Post by: D.Tkalcec (RTC) on September 10, 2018, 04:46:25 PM Normally, whatever you prepare will be sent out, so there are no events or methods on RTC components which would allow you to double-check what was prepared before it actually goes out, but ... you should be able to use
external Network Traffic monitoring tools like Wireshark to check what is REALLY being sent and received: https://www.wireshark.org/ Title: Re: sending a XML-file back with a TRctHTTPserver Post by: Henk vd Boogaard on September 11, 2018, 02:02:04 PM Hi Danijel
I have the program working. So I am very happy you and Henrick (StreamSec) helped me. Only one problem is now that on my pc is works fine but on the server (Windows Server 2016) it seems that the outgoing port 443 is blocked. But they are investigate that. Thanks Henk van den Boogaard Title: Re: sending a XML-file back with a TRctHTTPserver Post by: D.Tkalcec (RTC) on September 11, 2018, 06:39:03 PM Glad to know it's working. Thanks for your feedback.
|