Mega Code Archive

 
Categories / Delphi / .NET
 

Web Service Workshop with Remote Data Storing

Title: Web Service Workshop with Remote Data Storing Question: How do you transfer scanned client-data with a web service from a client to a database server or a file automatically and store it? Answer: Weeks ago I got a source code example called VCLScanner from Borland that scans your harddisk and sends a report to a server like a Remote Procedure Call over the web. My students were fascinated and put me in charge to write an article about the main topics about Web Services and how the source works. VCL Class Scanner will generate a class usage report based on Delphi and C++Builder applications located on your system and send the report to a http location. Now I'm going to explain the build an made a few modifications, so you can download and compile the source with Delphi6 and test the Web Service on your local host and your personal web server previously installed. What's a Web Service? ---------------------------------------------------------------------------- Delphis support for Web Services is designed to work using SOAP (Simple Object Access Protocol). SOAP is a standard lightweight protocol for exchanging information in a decentralized, distributed environment. It uses XML to encode remote procedure calls and typically uses HTTP as a communications protocol. ObjectPascals SOAP-based technology is available on Windows and will later be implemented on Linux, so that it can form the basis of cross-platform distributed applications. There is no special client runtime software to install, as you must have when distributing applications using CORBA. Because this technology is based on HTTP messages, it has the advantage that it is widely available on a variety of machines. Build the Server ---------------------------------------------------------------------------- Open please the group-file ProjectWServices.bpg. Let's have a look first in the server code so you can see the Web Service main methods in the interface IVCLScanner: type IVCLScanner = interface(IInvokable) ['{8FFBAA56-B4C2-4A32-924D-B3D3DE2C4EFF}'] function PostData(const UserData : WideString; const CheckSum: DWORD) : Boolean; stdcall; procedure PostUser(const Email, FirstName, LastName: WideString); stdcall; end; We can see (so I hope) the Web Service is able to PostData in a file or to PostUser in a database. Before a Web Service application can use this invokable interface, it must be registered with the invocation registry. On the server, the invocation registry entry allows the invoker component (THTTPSOAPPascalInvoker) to identify an implementation class to use for executing interface calls. All this goes with the Web Service Wizard so define the interfaces that make up your Web Service is easy (at least). Note: It is a good idea to create your interface definitions in their own units, separate from the unit that contains the implementation classes. In this way, the unit that defines the interfaces can be included in both the server and client applications. Now we made our own entries to store data in a file. The only thing you must do is changing the path of the FileName: function TVCLScanner.PostData(const UserData: WideString; const CheckSum: DWORD) : Boolean; var SL : TStringList; FileName : String; begin SL := TStringList.Create; FileName := 'D:\Franktech\Webservices\'+FormatDateTime('yyyymmdd- hhnnsszzz',Now)+'.txt'; SL.Text := UserData; SL.SaveToFile(FileName); SL.Free; Result := True; end; The same goes for the database configuration with dbExpress, please have a look at the help about configuring TSQLConnection and compare with the source code. Update 1: see below how to do it with Delphi 10! After configuration only one line left customising depending on your InterBase file location and the server name, in my example the same like the client machine (milo2): Params.Add('Database=milo2:D:\franktech\webservices\umlbank.gdb'); Compile and copy the Web Service to the Web Server ----------------------------------------------------------------------------- That's all for the server so we can compile it. Now comes the more interesting part. You have to copy the VCLScannerServer.exe in your web server's scripts directory, like a CGI-script or an NSAPI-DLL. Then you made a double click on the EXE and it will generate a file which goes like this: ------------------------------------------- VCLSCANNERSERVER_WSDLADMIN.INI [IWSDLPublish] IWSDLPublishPort=http:///soap/IWSDLPublish [IVCLScanner] IVCLScannerPort=http:///soap/IVCLScanner ------------------------------------------- This comes from the method (from path info soap*) WSDLHTMLPublish1.DispatchRequest(Sender, Request, Response); and we need this data to customize the client. The whole WSDL publisher publishes a WSDL document that describes your interfaces and how to call them. It enables clients that are not written using Delphi to call on your Web Service application. In our case (Delphi to Delphi on a local machine) we only need to fill the URL info path in our client main.pas. Build the Client ---------------------------------------------------------------------------- Note: On client apps, an invocation registry entry allows components to look up information that identifies the invokable interface and supplies information on how to call it. Next, we provide the THTTPRio object with the information it needs to identify the server interface and locate the server. All you need to do is supply the URL where you install the Web Service app. However, you may want to make your Web Service available to a wider range of clients. E.G., you may have clients that are not written in Delphi. If you are deploying several versions of your server app, you may not want to use a single hard-coded URL for the server, but rather let the client look up the server location dynamically. For these cases, you may want to publish a WSDL document that describes the types and interfaces in your Web Service, with information on how to call them. But in our case we only change the component THTTPRIO the URL property: http://milo2/scripts/VCLScannerServer.exe/soap/IVCLScanner //(milo2 or localhost is the web server) As I said, if the server is written in Delphi, the identification of the interface on the server is handled automatically, based on the URI that is generated for it when the interface is registered. Note: The path portion of this URL should match the path of the dispatcher component in the servers Web Module, that's why the information of the generated file is so important. For testing and time saving you can change line 310 FindFiles(dlbDirectory.Directory,Aborted); with a hard-coded file to scan like: FindFiles('D:\franktech\entwickl\comdll_intf',aborted); The client calls the web service trough an interface object: try WS := HTTPRIO1 as IVCLScanner; WS.PostData(reFinalResults.Text,CRC); Now if all goes well it writes some files on your disk with scanned data in it. When you have trouble with database configuration or running out of time (lost in space) deactivate line 1063: WS := HTTPRIO1 as IVCLScanner; WS.PostUser(leEmail.Text,leFirstName.Text,leLastName.Text); Conclusion ----------------------------------------------------------------------------- To be happy and give some contributions try also the original Borland URL: WSDLLocation = 'http://ww6.borland.com/webservices/VCLScanner/ VCLScannerServer.exe/wsdl/IVCLScanner' Hope you'll learn from the example, for further actions here a step be step to build a Web Service Server: 1 Define and implement classes that implement the invokable interfaces you defined. 2 If your application raises an exception when attempting to execute a SOAP request, the exception will be automatically encoded in a SOAP fault packet, which is returned. 3 Choose File|New|Other, and on the WebServices page, double-click the Soap Server application icon. Choose the type of Web server application you want to have implement your Web Service. 4 The wizard generates a new Web Service application that includes three components: An invoker component (THTTPSOAPPascalInvoker). The invoker converts between SOAP messages and the methods of any interfaces you registered. A dispatcher component (THTTPSoapDispatcher). The dispatcher automatically responds to incoming SOAP messages and forwards them to the invoker. A WSDL publisher (TWSDLHTMLPublisher). The WSDL publisher publishes a WSDL document that describes your interfaces and how to call them. 5 Choose Project|Add To Project, and add the units you created to your Web server application. Note: All hints about publishing or registering is get done at runtime, so no registry or helper-file is needed but you can use the admin-file to change at runtime ports or adresses (VCLSCANNERSERVER_WSDLADMIN.INI) Calling the Service from .NET goes like this: private void Page_Load(object sender, System.EventArgs e) { IVCLScanner.IVCLScannerservice objIVCLScanner = new IVCLScanner.IVCLScannerservice(); bool booScanner = objIVCLScanner.PostData("blabla",413049395); lblScanner.Text = "" + booScanner; } We're learning from day to day (more at night ;)) so wisdom is where knowledge ends. -------------------------------------------------------------- Update 1: dynamic dbexpress with Delphi 10 const StrDatabase2Dfr = 'Database=APSN21:D:\kleiner2005\ekon9_10\soa_vcl\umlbank2.gdb'; StrInsertSQLStat = 'insert into KINGS values ("%s", "%s", "%s")'; procedure TVCLScanner.PostUser(const Email, FirstName, LastName: WideString); var SQLConnection1: TSQLConnection; DataSet: TSQLDataSet; begin //webModule1.SQLConnection1.Open; SQLConnection1:= TSQLConnection.Create(NIL); with SQLConnection1 do begin ConnectionName := 'VCLScanner'; DriverName := 'INTERBASE'; //LibraryName := 'dbexpint.dll' in D6; LibraryName:= 'dbxint30.dll'; VendorLib:= 'GDS32.DLL'; GetDriverFunc:= 'getSQLDriverINTERBASE'; Params.Add('User_Name=SYSDBA'); Params.Add('Password=masterkey'); //Params.Add('Database=myserver:X:\vclscanner.gdb'); Params.Add(StrDatabase3Dfr); LoginPrompt:= False; Open; end; DataSet:= TSQLDataSet.Create(nil); with DataSet do begin SQLConnection:= SQLConnection1; CommandText:= Format(StrInsertSQLStat, [Email,FirstName,LastName]); try ExecSQL; //silent cause of CGI Webscript except end; end; SQLConnection1.Close; DataSet.Free; SQLConnection1.Free; end;