.NET Web Services II...
Part II: Customising attributes; Disco and UDDI practicalities and beginning asynchronous Web Services.
Knowledge assumed: VB.NET, VS.NET; .NET Web Service I
After reviewing the basics of Web Services including creating and consuming Web Services in Part I in this article we continue on to look at Web Services in more depth as follows:
Article II:
Customising the WebMethod attribute
Disco and UDDI practicalities
The disco.exe and wsdl.exe tools
Beginning asynchronous Web Services
In parts III and IV we'll look at the more complex areas of:
Creating and using SOAP extensions
Creating asynchronous web methods
Controlling XML wire format
Note that in part I the stated intention was to cover all the above in 3 articles. I've realised this is not possible and so intend to extend the series to 4 articles.
In part I we saw how easy VS.NET makes generating Web Services. Simply marking methods with the WebMethod attribute exposes the enclosed functionality to Web Services clients. You may further customise the exposed WebMethods via attributes.
| BufferResponse | Gets or sets whether the response for this request is buffered. If set to true the entire response is buffered on the server and returned to the client as a single message. If false the response is sent in 16K chunks. |
| CacheDuration | Gets or sets the number of seconds the response should be held in the cache. The default is zero, disabling caching. |
| Description | A descriptive message describing the XML Web Service method. |
| EnableSession | Indicates whether session state is enabled for an XML Web Service method. False is the default – disabled. |
| MessageName | The name used for the XML Web service method in the data passed to and returned from an XML Web Service method (the SOAP message). |
| TransactionOption | Indicates the transaction support of an XML Web Service method. If set to TransactionOption.Required or TransactionOption.RequiredNew allows the Web method to participate as the root object of a transaction. The default is TranactionOption.Disabled. |
For example we could alter the articles Web Service I wrote in the first article in this series, as follows.
|
<WebMethod(CacheDuration:=60, _ Description:="Method to demonstrate caching")> _ Public Function GetArticles() As DataSet Dim dsDMS As DataSet = New DataSet dsDMS.ReadXmlSchema(Server.MapPath("articles_schema.xml")) dsDMS.ReadXml(Server.MapPath("articles.xml")) Return dsDMS dsDMS = Nothing End Function |
This version will keep the results cached for 60 seconds and also specifies a description.
You will see you can use SessionState as per more conventional web applications. In common with such applications, perhaps even more so, be careful in the use of session state as a popular service means lots of resources used up for session state.
The only time you'll want to set BufferResponse to true is when you're returning more than 16K of data and that data is being constructed on the fly, for example from database data.
The transaction property has been largely supplanted by the facilities available via the WS-Transaction portion of the new Global XML architecture (GXA) specification – see MSDN for further information, as we shan't be covering this more advanced topic here.
As introduced in Article I in this series for VS.NET projects you won't ordinarily want to build a Disco document because such projects are designed to generate discovery information from their base URL, e.g.:
http://myServer/myWebService/base_class.asmx?wsdl
You may encounter the need to generate a static discovery document that does not require processing to create. You will notice that one of the items you may add to your Web Services project in VS.NET is a static discovery file. Do this, naming it appropriately, e.g. articles.disco. In this new file add code so that the complete code is:
|
<?xml version="1.0" encoding="utf-8" ?> <discovery xmlns="http://schemas.xmlsoap.org/disco/"> <contractRef ref="articles.asmx?WSDL" xmlns="http://schemas.xmlsoap.org/disco/scl" /> </discovery> |
In your client application you will now be able to add a web reference to this file. This may be viewed as the more 'correct' way of discovering a Web Service – a disco file pointing to the corresponding wsdl file. However, the add web reference facility of VS.NET circumvents this need identifying Web Services and locating the corresponding wsdl files for you.
Turning to UDDI, you may also need to register your Web Service with a private or public UDDI registry. To do so you must use the tools provided by the particular registry. We'll quickly run through an example using Microsoft test UDDI registry site located at http://test.uddi.microsoft.com.
To register your Web Service navigate to the just mentioned URL, click the register link and sign in using Microsoft Passport. If you don't have a Passport you'll need to follow the instructions to create one. You'll then need to create a publisher account as directed. You'll then be granted access to the Administration functionality where you can add 'tModels', which, as explained at the site are 'representations of a description of an interface and can be referenced as part of the binding and instance information for a service'. Fill in the required details including a reference to the wsdl document in the 'overview document' section. Others will now be able to locate your Web Service using this directory.
VS.NET automatically handles the discovery process for you when you add a web reference but you can also perform the necessary step yourself via the supplied Web Services Discovery tool, disco.exe, as follows.
Open a command prompt in the VS.NET environment (start-programs-VS.NET-Tools-Command prompt). The tool takes as a parameter the URL of the wsdl file of the Web Service. For example,
disco http://localhost/my_articles_ws/articles.asmx
The Web Services Discovery tool discovers the URLs of XML Web services located on a Web server and saves documents related to each XML Web service on a local disk.
The .wsdl, .xsd, .disco, and .discomap files produced by this tool can be used as input to the Web Services Description Language Tool (wsdl.exe) to create XML Web service clients.
In the above case the following files are generated:
articles.disco
articles.wsdl
results.discomap
If the Web Service includes a static discovery document, i.e. a disco file, as we have in this case from the earlier example this is also retrieved.
Take a look at these in VS.NET (you may need to show all files to view). results.discomap is an XML document containing the name of the other file (wsdl) and the URL from which its contents were generated. The wsdl file gives VS.NET the detail it needs to enable you to use a Web Service from your code.
Based on the information retrieved use the disco tool, i.e. the wsdl file you can generate the necessary proxy class that you can use to invoke the Web Service from a client application. This can be done with the wsdl.exe tool included in the .NET Framework SDK, as I'll now describe.
Return to the just used command prompt in the VS.NET environment, ensuring you are in the same directory as the generated wsdl file and enter the following to generate a proxy class:
wsdl /language:VB /out:articles_proxy.vb articles.wsdl
Add the generated file articles_proxy.vb to a new ASP.NET client application exactly as per part I of this article series (file- add existing item). Change the web form codebehind slightly to:
|
Private Sub btnInvoke_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnInvoke.Click Dim articles As Articles = New Articles dgArticles.DataSource = articles.GetArticles() dgArticles.DataBind() End Sub |
and view the page and you'll see the code works just as if you had created the proxy via the use of a web reference.
How is using web references different? It isn't really as behind the scenes the web reference creates its own proxy class. If you 'show all files' and expand the web references folder and sub folders you see some files with some now familiar extensions. The .disco, .wsdl and .map files are the same files that would be generated running the wsdl.exe tool on the URL of the Web Reference. The .vb file defines the proxy objects to be used with the Web Service represented by the web reference and is very similar to that generated with the wsdl tool.
The examples we've looked at thus far have all being locally hosted so you should not have experienced any significant delays between invoking your Web Service and receiving the results back through the client application. They can take a longer time however, whether this is due to the complexity of the operations being performed or simply the combination of the amount of data and speed of connection involved.
By default the proxy class the client is using uses synchronous methods to communicate with the Web Service, i.e. waiting for a SOAP response from the Web Service before continuing on to execute any further client code. Asynchronous communication is possible however, as I'll introduce now and as we'll explore in more depth in part III of this series of articles.
For each synchronous method, the generated proxy class also contains a corresponding Begin and End method. Calling a Web service asynchronously is a two-step operation. The first step, calling the Begin method, initiates the Web Service call. The second step, calling the End method, completes the Web Service call and returns the Web Service response.
The Begin method returns a System.Web.Services.Protocols.WebClientAsyncResult object. This object, which implements System.IAsyncResult, serves two purposes. First, it provides status about the pending asynchronous call. For example, the IsCompleted property indicates if the operation has completed. Second, by passing this object to the End method, the proxy can identify which request you would like to complete. This is important because you can make multiple asynchronous calls simultaneously.
There are several ways to determine when the asynchronous Web Service call has completed. In the example that follows we'll supply a callback delegate to the Begin method.
The example is implemented as a Windows form rather than a Web form primarily to simplify the implementation. If you think about it the Web form may have been delivered by the web server before the Web Service has had a chance to process the request.
We're going to use another Web Service, even simpler than the example we've used up until now. This Web Service will expose one Web method, ToUpper, which will convert a string to upper case. It's so simple I shall leave coding of this Web Service as a task for the reader!
Create a new Windows form project and add 3 items from the toolbox: 2 text boxes and a label, named txtInput, txtOutput and btnUpperCase. Specify the text of the label appropriately, e.g. 'Convert to upper case'). Add a web reference to your just created Web Service. The code below assumes the Web Service is available on your localhost and is named Strings – amend the code if this is not the case. Add the following code to your Windows form:
|
Private strUpperCase As String Private Sub btnUpperCase_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnUpperCase.Click Dim objStrings As localhost.Strings = New localhost.Strings Dim wcb As New AsyncCallback(AddressOf WebServiceCallback) objStrings.BeginToUpper(txtInput.Text, wcb, objStrings) End Sub Public Sub WebServiceCallback(ByVal ar As IAsyncResult) MsgBox("WebServiceCallback called") Dim objStrings As localhost.Strings = ar.AsyncState txtOutput.Text = objStrings.EndToUpper(ar) End Sub |
Run the project. You'll see you can drag the form around the screen whilst the call is being processed – the program is not locked from undertaking other activities - and you'll get a msgbox displayed as well as the converted text string when the Web Service result is returned to the client.
Examining the code, the Begin method takes the same parameters as the underlying Web method plus an additional two: the address of the callback function and the object whose properties need to be available in the callback function.
Now, when the Begin method is called the .NET Framework makes the call to the Web Service in the background. When the Web method call completes the callback function will be invoked. The final couple of lines of the WebServiceCallback function then simply indicates to the user that the call is complete and displays the result.
We'll return to this topic in the final article in this series.
That concludes part II of our look at Web Services. In this article we've delved a little deeper into the world of Web Services looking at customising attributes and the practicalities of Disco and UDDI as well as introducing asynchronous communication with Web Services which will be a topic we look at again in part IV of this series, along with XML wire formats. In the next article, part III of the series, we look at SOAP extensions.
.NET SDK
Developing XML WebServices and Server Components with VB.NET and the .NET Framework
Mike Gunderloy
Que
MSDN