Underneath all the hype, Web Services are about exposing functionality using
standard Internet protocols such as HTTP and XML technologies. Unlike previous
binary communication formats (eg. DCOM), this framework of standard technologies
opens the door for new levels of interoperability between disparate systems.
The .NET Framework leverages ASP.NET to provide a simple mechanism for developing web
services. To justify this claim, take a look at the following code sample.
<%@ WebService
language="C#"class="Calc1"%>
using
System; using
System.Web.Services;
Saving this code in a file with an ASMX extension, and placing it on the web server is all that
is needed to expose your first web service. If you load the page in a browser, the ASP.NET framework
will generate some help information about your service, and will even generate a form for testing your
service via the HTTP-GET protocol.
Using Attributes
There are several attributes you
can use to decorate web service methods. The one that you will almost always
find is the [WebMethod] attribute, which in an indicates that a class method is
an XML Web Service method. The following table outlines some of the more common
attributes you can use:
WebMethod
Indicates a method is an XML Web service method.
WebService
Applied at the class level to provide more information about the Web Service,
such as a description and default namespace.
SoapDocumentMethod
Indicates that the XML Web Service method expects Document-based SOAP messages.
SoapRpcMethod
Indicates that the XML Web Service method expects RPC-based SOAP messages.
SoapHeader
Indicates that the XML Web Service method can process a specific SOAP Header.
SoapExtension
Indicates a SOAP extension should execute when the XML Web Service method is
called (Note: This attribute is abstract. You have to create a custom attribute
deriving from this class for your SOAP extension).
For example, the following code snippet from a web service shows a Web Service
method that accepts a SOAP Header, and expects SOAP messages in Document
format.
[WebMethod, SoapHeader("aSoapHeader"), SoapDocumentMethod]
public string GetProductName()
SOAP Extensions
SOAP Extensions are used to plug
into the message framework involved in SOAP Requests and Responses, and run your
custom code to view and/or alter the SOAP messages. Uses for SOAP Extensions
include encrypting/decrypting communications, or logging messages sent to and
from a service.
The following diagram, taken from the .NET Framework SDK
Documentation, outlines the steps involved in a Web Service Request/Response
communication. It's important to understand the order of the phases when working
with SOAP Extensions.
SOAP Extensions
can be create to inspect and modify the SOAP message before and after the
Serialize and Deserialize stages. These opportunities are defined in the
SoapMessageStage enumeration. Possible values for this enumeration are:
Value
Description
AfterDeserialize
The stage just after a SoapMessage is deserialized from a SOAP message into an
object.
AfterSerialize
The stage just after a SoapMessage is serialized, but before the SOAP message
is sent over the wire.
BeforeDeserialize
The stage just before a SoapMessage is deserialized from the SOAP message sent
across the network into an object.
BeforeSerialize
The stage just prior to a SoapMessage being serialized.
Now that you understand what a SOAP Extension is used
for, and the Request/Response process, you can write a SOAP Extension. There is
no special Visual Studio.NET project for creating a SOAP Extension, so you have
to create the code manually. The steps you need to follow are:
1. Derive a class from SoapExtension The SoapExtension class is the
base class for all SoapExtensions. You will need to inherit your SOAP Extension
from this class.
2. Override the ChainStream method
Prior to any SoapMessageStage, the ChainStream method of the SOAP Extension is
called and a Stream reference is passed in. This Stream is used to access the
contents of the SOAP Message, which may have been modified by lower priority
SOAP Extensions. Your SOAP Extension should save a reference to this object.
This method also returns a Stream, although you should not use the Stream
passed in as a parameter. Rather, create a new Stream, also saving that as a
member variable, and return that. This way, you can both access the original
stream and write modifications to the new stream. Here is an implementation in
C# of an overridden ChainStream method:
publicoverride
Stream ChainStream(Stream
stream) { //Save the Old Stream Locally m_OldStream=stream;
//Create a New Stream, and Save it Locally m_NewStream=new
MemoryStream();
//Return the New Stream returnm_NewStream; }
3. Initialize the SOAP Extension There are two methods for initializing a SOAP Extension. The first is
GetInitializer, and it is called in instances where the SOAP Extension is
configured using an attribute, then the method is called once when XML Web
Services are called on a per method basis. If you have 3 methods, the
GetInitializer would be called 3 times, once when each of the web service
methods was invoked. If however, the SOAP Extension is configured in a
configuration file, then the GetInitializer method is called by the .NET
Infrastructure only when the XML Web Service is first accessed, and not on a per
method basis. The data returned from the GetInitializer method is cached, and
passed to the Initialize method each time a web service method is invoked. This
two-method process forces caching of data to eliminate the costly initialization
time that services can have.
4. Process the SOAP Messages
Processing the SOAP Messages is what a SOAP Extension is all about, right? The
key method then is the ProcessMessage method. This method has a single
parameter: a SoapMessage object. It's important to note too that this method
will be called for each of the stages defined in the SoapMessageStage
enumeration: BeforeSerialize, AfterSerialze, BeforeDeserialize, and
AfterDeserialize. You can determine the current stage by checking the
SoapMessage parameter's Stage property.
//Process the Message publicoverridevoid
ProcessMessage(SoapMessage
message) { //Check the Stage and Do Some Work switch(message.Stage) { case
SoapMessageStage.BeforeSerialize: break; case
SoapMessageStage.AfterSerialize: break; case
SoapMessageStage.BeforeDeserialize: break; case
SoapMessageStage.AfterDeserialize: break; } }
Now that you have a SOAP Extension, you need to
configure it to run with an web service method. There are two ways to configure
SOAP Extensions: with attributes on the WebMethod, or via a configuration file.
To configure an extension using attributes, you need to derive a class from
SoapExtensionAttribute. This class has two properties, ExtensionType and
Priority. ExtensionType should be set to the Type of your Soap Extension. You
can these use this custom attribute on xml web methods to attach your extension.
The second method of indicating SOAP Extensions is via a
configuration file, such as the web.config file. Using a configuration file will
affect all web service methods in the scope of the configuration file. Under the
<system.web> tag, add a <webServices> tag, and under that add a
<soapExtensionTypes> tag. Within the <soapExtensionTypes> tag, you
can insert one or more <add> tags to indicate SOAP Extensions. The syntax
for this tag is shown in the example below:
An asynchronous call
to a web method requires no special asynchronous support by the Web Service
code. Rather, the framework necessary is found within the Proxy object created
by the Web Services Description Language (WSDL) utility. The proxy object
created by this utility implements 3 methods for every 1 method in the web
service: a synchronous method with the same name, a Begin[MethodName] method for
initiating an asynchronous call, and an End[MethodName] method for getting the
result from an asynchronous call.
For example, the following code snippet comes from the proxy object generated for
the Amazon.com Web Service. Notice that for the KeywordSearchRequest web
service method, there are actually 3 methods generated in the proxy class.
//Asynchronous
Begin Method public
System.IAsyncResult BeginKeywordSearchRequest(KeywordRequest
KeywordSearchRequest1,
System.AsyncCallback
callback,objectasyncState) { returnthis.BeginInvoke("KeywordSearchRequest",newobject[]{KeywordSearchRequest1},callback,asyncState); }
//Asynchronous End
Method public
ProductInfo EndKeywordSearchRequest(System.IAsyncResult
asyncResult) { object[]results=this.EndInvoke(asyncResult); return((ProductInfo)(results[0])); }
There are two ways for implementing client calls to
asynchronous methods. The first involves using the IAsyncResult return value
from the Begin method to periodically poll the status of the request using the
AsyncWaitHandle property (type WaitHandle). The WaitHandle class has methods
WaitOne, WaitAny, and WaitAll for blocking the client until the web method call
is complete. The IAsyncResult object also has a property, IsCompleted, that
returns true when the asynchronous call has been completed, and the results are
available. To get the results of the asynchronous call, the client needs to call
the End method. The second way of implementing the call is by using a callback
delegate. By passing an AsyncCallback object to the Begin method, the callback
method will be called when the asynchronous method is completed. In the callback
function, you can get the results by calling the proxy object's End method.
The following example shows the use of a Callback function to handle the completion
of the asynchronous call.
using
System; using
System.Runtime.Remoting.Messaging; usingcom.dotnetcoders.www;
class
TestCallback
{ publicstaticvoid
Main() { //Create AsyncCallback delegate for the
BeginAdd method. AsyncCallback
objCallBack=new
AsyncCallback(TestCallback.FactorizeCallback1);
// Begin the Asynchronous call to the Web
Service Calc1objCalcService=new
Calc1();
IAsyncResult
objAsyncResult=objCalcService.BeginAdd(5,5,objCallBack,objCalcService);
// Keep track of the time it takes to
complete the async call intstart=
DateTime.Now.Second; intcurrentSecond=start; while(objAsyncResult.IsCompleted
==false) { if(currentSecond
< DateTime.Now.Second) { currentSecond=
DateTime.Now.Second;
Console.WriteLine("Seconds
Elapsed..."+(currentSecond-start).ToString()); } }
// Pause this thread and wait for Callback to
finish Console.Write("Press any
key to quit...");
Console.Read(); }
// Callback function invoked by the proxy class when the
asynchronous operation completes publicstaticvoid
FactorizeCallback1(IAsyncResult
objAsyncResult) { // Use the AsyncState property to get the Web
Service proxy object Calc1objCalcService=(Calc1)objAsyncResult.AsyncState;
// Get the completed
results. intresults=objCalcService.EndAdd(objAsyncResult);
//Output the results. Console.Write("5 + 5 =
"+results); } }
XML Wire Format
There are three common protocols used when working with XML Web Services:
HTTP-GET, HTTP-POST, and SOAP.
HTTP-GET
Parameters are passed as name/value pairs in the URL, appended as a
querystring.
HTTP-POST
Parameters are passed as name/value pairs in the HTTP header of the request.
SOAP
Call is made using the lighweight, xml-based SOAP protocol.
When using a proxy object generated by wsdl.exe, SOAP is the protocol. Because
SOAP is based on XML technologies, data types for variables can be defined
using XSD, whereas with HTTP, all variables are unvalidated strings.
Instantiating and Invoking
For the exam, you should be familiar with invoking Web Services as well as
creating them.
To instantiate and invoke a web service, follow these steps:
Add a Web Reference - Select Project->Add Reference, and enter in the path
to the WSDL for the web service
Instantiate the .NET proxy object
Call methods on the proxy object
When you add a Web Reference, Visual Studio will use the Web Services
Description Language Utility (wsdl.exe) utility to create a proxy object for
the web service.
You can see this reference in Solution Explorer under the Web References
tab. Then, you create an instance of the proxy object like any other .NET
object. For example, after adding the Web Reference, the following code calls
the Add method on the Calculator service.
You can also call the Web Services Description Language (wsdl.exe) utility from
the command line, passing it the location of the WSDL, and the utility will
generate a proxy object for you.
When calling Web Services, you also have the option of calling web methods
asynchronously. The methods that make this possible are generated automatically
by the wsdl.exe utility in the proxy object. The naming convention for
asynchronous web methods is Begin[MethodName] and End[MethodName]. For example,
the following screenshot shows the methods of the Amazon.com Web Service.
Notice that there is an AuthorRequest method for synchronous calls, and a
BeginAuthorRequest method for asynchronous calls.
In order to call the web method asynchronously, supply a callback delegate to
the Begin[MethodName] method. When the web service returns it's response, a
thread will invoke your callback function. See the section above,
Asynchronous Web Methods,
for more information on calling web service methods asynchronously.
In the event that the author of the web service changes their service after you
have already added a reference to your project, you can just right-click the
service in the Solution Explorer, and select Update Reference. This will
re-generate the proxy object with the updated methods.