.NET SOAP Classes
.NET provides a complex yet elegant system that you can use to implement Web Services. From a programming model perspective, a Web Service in .NET is little more than a method in a class. You simply slap on a couple attributes, and, voila[ag] you have a Web Service. And that's a good thing. We want that. But it's also nice to look under the hood to see what's running this machine.
The .NET SoapFormatter Class
SoapFormatter is one workhorse .NET SOAP class. Its purpose in life is to format a generic .NET object using a SOAP format, given a stream. The class itself has several methods, but the two most interesting are Serialize() and Deserialize(). Serialize() takes as input both a stream into which the SOAP output will be formatted and an object to format. Deserialize() takes the stream from which an object will be created.
To demonstrate SOAP formatting in .NET, Listing 4.2 provides you with a short C# console application that formats the jagged array example into a SOAP format, saves it to disk, and deserializes the array.
Listing 4.2 SoapFormatter Demonstration
using System; using System.IO; using System.Runtime.Serialization.Formatters.Soap; namespace SoapSerializer { /// <summary> /// Summary description for SoapSerializerTester. /// </summary> class SoapSerializerTester { bool Serialize(string strPath, Object objStuff) { bool bReturn = true; try { // Create a file stream FileStream fstmOutput = new FileStream(strPath, FileMode.Create); // Create the formatter we'll associate // with this stream SoapFormatter sfmtFormatter = new SoapFormatter(); // Serialize and save the object sfmtFormatter.Serialize(fstmOutput,objStuff); // Close the file stream fstmOutput.Close(); } //try catch(Exception e) { Console.WriteLine("{0}",e.Message); bReturn = false; } // catch return bReturn; } Object Deserialize(string strPath) { Object objReturn = null; try { // Create a file stream FileStream fstmInput = new FileStream(strPath, FileMode.Open); // Create the formatter we'll associate // with this stream SoapFormatter sfmtFormatter = new SoapFormatter(); // Deserialize and return the object objReturn = sfmtFormatter.Deserialize(fstmInput); // Close the file stream fstmInput.Close(); } //try catch(Exception e) { Console.WriteLine("{0}",e.Message); objReturn = null; } // catch return objReturn; } static void Main(string[] args) { try { // Create an instance... SoapSerializerTester sst1 = new SoapSerializerTester(); // Create a jagged array to serialize/ // deserialize int[][] q = {new int[2], new int[4], new int[3]}; q[0][0] = 4; q[0][1] = 7; q[1][0] = 15; q[1][1] = 72; q[1][2] = 6; q[1][3] = 167; q[2][0] = 1; q[2][1] = 90; q[2][2] = 659; // Serialize the jagged array if ( !sst1.Serialize("c:\\out.txt",(Object)q) ) { Console.WriteLine("Failed to serialize object..."); return; } // if int[][] r = (int[][])sst1.Deserialize("c:\\out.txt"); if ( (Object)r == null ) { Console.WriteLine("Failed to deserialize object..."); return; } // if // Write results... Console.WriteLine("Result was {0}",r.ToString()); Console.WriteLine(" [0][0] = {0}",r[0][0]); Console.WriteLine(" [0][1] = {0}",r[0][1]); Console.WriteLine(" [1][0] = {0}",r[1][0]); Console.WriteLine(" [1][1] = {0}",r[1][1]); Console.WriteLine(" [1][2] = {0}",r[1][2]); Console.WriteLine(" [1][3] = {0}",r[1][3]); Console.WriteLine(" [2][0] = {0}",r[2][0]); Console.WriteLine(" [2][1] = {0}",r[2][1]); Console.WriteLine(" [2][2] = {0}",r[2][2]); Console.ReadLine(); } // try catch (Exception e) { Console.WriteLine("{0}",e.Message); } // catch } } }
If you run this code and open out.txt, you'll find the SOAP output shown in Listing 4.3.
Listing 4.3 SoapFormatter Output SOAP XML
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:a1="http://schemas.microsoft.com/clr/ns/System"> <SOAP-ENV:Body> <SOAP-ENC:Array SOAP-ENC:arrayType="a1:Int32[][3]"> <item href="#ref-2"/> <item href="#ref-3"/> <item href="#ref-4"/> </SOAP-ENC:Array> <SOAP-ENC:Array id="ref-2" SOAP-ENC:arrayType="xsd:int[2]"> <item>4</item> <item>7</item> </SOAP-ENC:Array> <SOAP-ENC:Array id="ref-3" SOAP-ENC:arrayType="xsd:int[4]"> <item>15</item> <item>72</item> <item>6</item> <item>167</item> </SOAP-ENC:Array> <SOAP-ENC:Array id="ref-4" SOAP-ENC:arrayType="xsd:int[3]"> <item>1</item> <item>90</item> <item>659</item> </SOAP-ENC:Array> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
The output is quite faithful to the SOAP specification! Note that the array is of type Int32 rather than int, but this is a result of .NET serializing .NET datatypes. If the remote Web Service cannot handle Int32 datatypes (or cannot map them to 32-bit integers), you might have to adjust the SOAP packet as it leaves your local machine.
.NET SOAP Framing Classes
We won't discuss all the .NET SOAP packet framing classes in great detail here. We'll save the details for Chapter 6, where you'll see a lot of code that uses those classes as you create Web Services and twiddle with the SOAP formatting without having to drop into raw XML to make changes.
NOTE
These classes are called framing classes because they support the frame, or the layout, of the SOAP packet. This is a contrast to the actual serialization classes that manage the serialization and formatting of data that is to be placed into the SOAP packet (SoapFormatter being one such class).
Nonetheless, Table 4.2 identifies several of the major classes and their uses within the .NET Web Service framework.
Table 4.2 .NET SOAP Packet Framing Classes
Class |
Purpose |
WebMethodAttribute |
Identifies a given class method as one that is specifically anointed with Web Service status and establishes some basic SOAP and WSDL information. |
WebServiceAttribute |
Identifies a given class as one that supports Web Services and establishes some basic SOAP and WSDL information. |
SoapAttribute |
Provides default functionality of the SOAP-based attributes (there are several). |
SoapBinding |
Allows you to tailor several aspects of the SOAP and WSDL XML, such as the SOAP and SOAP Encoding namespaces, the HTTP transport, and the WSDL binding. |
SoapBodyBinding |
Allows you to tailor other aspects of the SOAP and WSDL XML, such as the WSDL parts and document style (literal or rpc). |
SoapExtension |
Provides a point of extensibility within the .NET Web Service framework that allows you to access (and modify) the raw SOAP XML. |
SoapFault |
Encapsulates (and returns to the client) a SOAP fault packet. |
SoapHeader |
Encapsulates a given SOAP Header entry. |
SoapHeaderBinding |
Allows you to tailor other aspects of the SOAP and WSDL XML, such as the WSDL parts and the SOAP mustUnderstand status (true or false). |
SoapMessage |
Encapsulates the SOAP packet (used from within SOAP extensions). |
SoapMethodAttribute |
Used to tweak the SOAP XML (request and response namespaces and so on). This attribute creates literal SOAP (nonSections 5/7 formatted). This class is related to SoapRpcMethodAttribute, which gives you the same functionality but uses the rpc WSDL document style (forces use of Sections 5/7 for SOAP serialization). |
Other important SOAP classes exist, but these are the common ones that you'll most likely use when creating your .NET Web Services. You'll see these classes again in Chapter 6, where you'll use them when actually creating SOAP packets for transmission to the remote system.