|Delphi Clinic||C++Builder Gate||Training & Consultancy||Delphi Notes Weblog||Dr.Bob's Webshop|
Consuming Web Services with Delphi Prism
In the previous article, I've created an ASP.NET SOAP Web Service using Delphi Prism, an deployed it on my web server at http://www.bobswart.nl/DelphiPrismWebService/Service.asmx. In this follow-up article, I'll demonstrate how to consume and use this web service using Delphi Prism again.
Once the web service is deployed on the deployment web server, we can example the formal defnition, also called Web Service Definition Language or WSDL.For ASP.NET web services, you get the WSDL by appending ?WSDL to the URL of the web service, so that's http://www.bobswart.nl/DelphiPrismWebService/Service.asmx?WSDL for our example.
Note the description, which is placed in a wsdl:documentation tag, including the HTML tags that we added.
Web Service Client
In practice, you will often need to write a client for a Web Service application. This is also possible with Delphi Prism, and is not diretly related to Web Site projects anymore.Importing (or consuming) Web Services can be done in just about any Delphi Prism project target.
For this simple example, let's use a WinForms GUI, so right-click on the solution, select Add | New Project and in the Delphi Prism, Windows category, select a Windows Application.Specify MyWebServiceClient as Name (which will also become the namespace of the new project).
In the new project, right-click on the References node, and select Add Web Reference.In the dialog that follows, we can browse to web services in this solution, connect to web services on the local machine, or use UDDI servers on the local network.
While that's fine during development, it's less useful in practice, since I do not want to write a WinForms application that connects to the local network, but one that actually connects to the deployed web service. So instead of using the local network URLs, I just enter the the URL of our WSDL in the URL textbox and press enter:
Inside the dialog, we now see the human readable description of our service (with HTML tags), including the two methods (and their description, if we had added one). Note that the web reference namespace will be the namespace of our project (MyWebServiceClient) followed by the URL of the domain in reverse order (nl.bobswart.www in this case).
Once we click on the Add Reference button, a new Web Reference node is added to the project, with a subnode called nl.bobswart.www two sub-subnodes called Reference.map and Service.wsdl, with a file Reference.pas as leave node for the Reference.map.This Reference.pas, defining the MyWebServiceClient.nl.bobswart.www namespace, contains the Delphi Prism import of the web service.
In order to use the web service, place a Button control on the Main form, and write the following code (note that the namespace has to be added to the uses clause as well):
implementation uses MyWebServiceClient.nl.bobswart.www; method MainForm.button1_Click(sender: System.Object; e: System.EventArgs); var MyService: Service; begin MyService := new Service(); MessageBox.Show( MyService.Num2Word(1964) ); end;We only have to create a new instance of the Service, and then we can use it and call its methods as if it was a local object.Each call, however, will be passed on from the client to the server.In a synchronous way, by the way. Which means that the application is "frozen" while it's waiting for the call to the web method to return.
Sometimes, a call to a web method can take time - more time than you'd like your application to be waiting (especially since the normal call is synchronous and hence "blocking"). In those cases, you may want to consider calling the web method in an asynchronous way.
The benefit of calling a web method asynchronously is that the application itself does not appear frozen, while waiting for the call to the web method to return. Of course, if the web method is called in response to a button click event, then it would be wise to temporarily disable the button to avoid multiple web method calls to queue up.
In order to call a web method asynchronously, we should not call the method directly, but instead call the Num2WordAsync method. If you check the source code for the Reference.pas unit (generated by importing the web service), you'll notice several methods related to calling the Num2Word method asynchronously:
method Num2Word(value: System.Int32): System.String; event Num2WordCompleted: Num2WordCompletedEventHandler; method Num2WordAsync(value: System.Int32); method Num2WordAsync(value: System.Int32; userState: System.Object); method CancelAsync(userState: System.Object); reintroduce;The first method is the normal synchronous way of calling Num2Word. There are two Num2WordAsync methods: apart from passing the value to convert, the last one is also passing a userState object. We can use this userState object in the Num2WordCompleted event handler if that's required (for example to assign the result of the conversion to). The event Num2WordComplete will be called when the web method call is completed. We should assign an event handler to this event to respond to the fact that web method is finished.
The first choice is a good one, so select Num2WordCompletedEventHandler. As a result, not only will we get an assignment, but a new event handler will be added to the code in the code editor as well, result in the following:
method MainForm.button1_Click(sender: System.Object; e: System.EventArgs); var MyService: Service; begin MyService := new Service(); MyService.Num2WordCompleted += MyService_Num2WordCompleted; MessageBox.Show( MyService.Num2Word(1964) ); end; method MainForm.MyService_Num2WordCompleted(sender: Object; e: Num2WordCompletedEventArgs); begin end;Inside the newly created event handler, we can now use the e parameter of type Num2WordCompletedEventArgs to get to the result of the web method call. But before we can use that, we should move the reference to the import unit MyWebServiceClient.nl.bobswart.www from the implementation to the interface section (because the event handler declaration in the MainForm class needs this import unit for the Num2WordCompletedEventArgs type).
method MainForm.MyService_Num2WordCompleted(sender: Object; e: Num2WordCompletedEventArgs); begin MessageBox.Show( e.Result ); end;Apart from that, we should also change the synchronous call to Num2Word to the asynchronous call Num2WordAsync. And while we're at it, we should disable the button when the call starts, and enable it again when the call is completed, resulting in the following complete code for the asynchronous example:
method MainForm.button1_Click(sender: System.Object; e: System.EventArgs); var MyService: Service; begin (sender as Button).Enabled := False; MyService := new Service(); MyService.Num2WordCompleted += MyService_Num2WordCompleted; MyService.Num2WordAsync(1964); end; method MainForm.MyService_Num2WordCompleted(sender: Object; e: Num2WordCompletedEventArgs); begin MessageBox.Show( e.Result ); button1.Enabled := True; end;When you click on the button, it will immediately become disabled (preventing you from clicking again), but the application itself will remain responsive; you can move it around, resize it, click on other controls, etc. until the web method call is completed and the message box will show up (with the button still disabled).
The asynchronous way of calling web methods is slightly more work, but will result in more responsive and appreciated user interfaces that do not become "frozen" while the web methods are executing. As a result, I recommend using the asynchronous approach wherever possible.
In this article, I've demonstrated how we can import and use Web Services in Delphi Prism Applications.I've shown the easy synchronoys way of calling web methods, but also explained the benefits of and demonstrated how to call web methods in an asynchronous way.
Feel free to create and deploy your own ASP.NET Web Services with Delphi Prism, or consume my test Web Services at my website to convert numbers to Dutch words if you like.
For more information on SOAP and Web Services with Delphi, you can check out my Delphi 2006 XML, SOAP and Web Services on Lulu.com, or my RAD Studio 2007 XML, SOAP and Web Services courseware manual in PDF format (with free updates and e-mail support), or wait for the Delphi Prism Development Essentials manual to become available which will also include ASP.NET Web Service coverage.