Implementing WS-Attachments in a Web Service


Microsoft has chosen to support DIME both in Web Services Enhancements (WSE), a solution for developing secure, and routable Web services using the Microsoft .NET platform, and in version 3.0 of the Microsoft SOAP Toolkit. Since this book deals with Microsoft .NET, I ll examine how WSE 2.0 can be used to send and receive SOAP messages with attachments using DIME.

WSE Support for Attachments

WSE supports DIME for sending attachments with SOAP messages in a Web service, and it supports reading and writing DIME messages from and to an IO stream. However, WSE does not fully support the WS-Attachments specification or any of the proposed WSDL extensions for using DIME with Web services. In this section, I ll take you through the process of adding a binary attachment to a SOAP message in ASP.NET. Later, I ll briefly cover the stream support for DIME in WSE.

How the WSE Runtime Implements DIME

For SOAP messages with attachments, the WSE runtime implements a DIME-compliant message parser that translates the records of an incoming DIME message and extracts the primary SOAP message from the first DIME record and any encapsulated files from successive records as attachment objects. After being extracted from DIME, the primary SOAP message is then passed to the WSE message pipeline, where the series input filters evaluate the SOAP message for any other existing SOAP headers. Figure 3-2 shows how an incoming DIME message is handled by the WSE runtime and ASP.NET.

click to expand
Figure 3-2: Processing an incoming DIME message

For outbound SOAP response messages with attachments, WSE s DIME- compliant message parser builds a new DIME message in which the first DIME record is the outbound SOAP response and any specified attachments are encapsulated in subsequent DIME records. Figure 3-3 shows how a DIME message is assembled by the WSE runtime.

click to expand
Figure 3-3: Processing an outbound DIME message

Programmatic control of the WSE runtime and its support for DIME is implemented through the WSE API, in which objects in the Microsoft.Web.Services.Dime namespace provide DIME support. To use the DIME support provided by WSE, simply enable WSE for the Web service or client as described in Chapter 2. With WSE enabled, when incoming DIME messages conform to the WS-Attachments specification, the WSE runtime treats them as SOAP messages with attachments by extracting the primary SOAP message and all of its attachments. For each attachment, a DimeAttachment object is added to the DimeAttachmentCollection . These attachments are accessed through the SoapContext.Attachments property. Similarly, attachments can be added to the DimeAttachmentCollection for the SoapContext of an outgoing message so that the WSE runtime will include them as DIME records in the outgoing message.

Now let s walk through the process of creating a Web service response as a DIME message with attachments.

Adding Attachments to a SOAP Message Using WSE

When using WSE, both Web services and Web service-consuming client applications can create DIME messages with attachments. Of course, for the DIME-based messaging exchange to work, WSE or some other DIME-aware application must be enabled at each endpoint. Consider the simple document service class DocumentService with a public method that returns some number of XML documents along with the SOAP response message as attachments in a DIME message.

Note

In these DIME examples, namespace aliases have been created with the using directive as follows :

 using System.Web.Services; 
using System.Xml;
using Microsoft.Web.Services.Dime;
using Microsoft.Web.Services;

In this example, the DocumentService class is defined as follows:

 [ WebService(Description="Stores XML documents for 
+ search and retrieval",
Namespace="http://example.com/documentservice)]
public class DocumentService :
System.Web.Services.WebService
{

The GetDocument method implements the Web method that returns the requested XML documents.

 [WebMethod(Description = Retrieves XML documents using 
+ DIME", EnableSession = false)]
public string[] GetDocument(string[] docNames)
{

In this Web method, the SOAP request message from the client contains a collection of document names . This method in turn calls a private method named AddAttachment :

 private DimeAttachmentCollection 
AddAttachment(string[] docNames)
{
// Instantiate an attachment collection
DimeAttachmentCollection attachmentCollection = new
DimeAttachmentCollection();

// Try to get each document named in the array
for (int i=0; i<docNames.Length; i++)
{
// Location of the document file
string filePath = "c:\storage\" + docNames[i];

if (File.Exists(filePath))
{
// Add the document as an attachment to the collection
attachmentCollection.Add(new DimeAttachment(
docNames[i],"text/xml",TypeFormatEnum.MediaType,
filePath));
}
}

// If we fail to retrieve any documents, throw an error.
if (attachmentCollection.Count < 1)
{
throw new ApplicationException(
"no documents were found");
}

return attachmentCollection;
}

For each name in the docNames array, the Web service retrieves an associated XML file from the C:\storage folder and adds it to the DimeAttachmentCollection for the SoapContext . When the Web method returns, the WSE runtime makes the response message the primary DIME record and each DimeAttachment a subsequent record, in the order in which they were added to the DimeAttachmentCollection .

Extracting Attachments from a DIME Message

As before, any Web service endpoint, either client or service, can use WSE to receive and handle DIME messages provided that WSE has been enabled. When a DIME message, which conforms to WS-Attachments, is received, WSE reads each DIME record. The first record is the SOAP message, which is passed to the next input filter, and each subsequent attachment is buffered as a DimeAttachment object. The following example is based on the DIME message returned from the previous GetDocument Web method, where the attachments in the DIME message contain a set of XML documents that are streamed from each attachment in the DimeAttachmentCollection into a corresponding XmlDocument object in an array:

 private static XmlDocument[] 
GetAttachments(SoapContext myContext, string[] docNames)
{
// Get the attachment collection from the passed
// SOAP context
DimeAttachmentCollection myAttachments = myContext.Attachments;

// Instantiate an array for the XML documents
XmlDocument[] myDocs;

// Check if response message contains any attachments
if (myContext.Attachments.Count > 0)
{
// Set the length of the array based on the
// number of attachments
myDocs = new XmlDocument[myAttachments.Count];

// Stream each attachment into an XML Document
// and then add it to the array
for (int i=0; i < myContext.Attachments.Count; i++)
{
if (myContext.Attachments[i].Type == "text/xml")
{
myDocs[i].Load(myContext.Attachments[i].Stream);
}
}
return myDocs;
}
return null;
}

In this example, each attachment is written from memory into a new XmlDocument object as a data stream by using the DimeAttachment.Stream property. While it may be trivial to send XML documents as attachments when they could easily be included in the message body, this same process can be used to send any other type of binary attachment file as long as a proper media-type identifier is used.

Advanced DIME Support in WSE

Whereas WSE allows you to easily encapsulate attachments with outgoing SOAP messages, the WSE support for DIME provides some more advanced capabilities. Although really designed more for non-SOAP implementations , they re interesting and, therefore, worth describing.

Chunking Large Attachments

As you might remember from this book s previous discussion of DIME, the DIME specification supports the ability to chunk large attachments into multiple records. The theory behind chunking is that by breaking a large attachment into chunks , you don t have to buffer the entire attachment to write it to a single DIME record. WSE lets you specify the chunking size of attachments in bytes by setting the DimeAttachment.ChunkSize property. However, because the implementation of the HTTP protocol doesn t support streaming, when you use WSE with ASP.NET, the entire DIME message gets buffered in memory, which limits the usefulness of chunking in this implementation. A better method for transporting large attachments using DIME is streaming DIME over an appropriate transport. WSE does support DIME over TCP/IP, but since using non-HTTP transports is outside the scope of this book, I will discuss the streaming of DIME in a more generic way.

Streaming Data Using DimeReader and DimeWriter

Although DIME is a message-based format, the fact that DIME records are serialized and can be chunked means that DIME can be used effectively over packet-level transport protocols, such as TCP and User Datagram Protocol (UDP). WSE supports the streaming of DIME messages to and from System.IO.Stream objects, such as NetworkStream , rather than being limited by a message-based protocol such as HTTP. When streaming as DIME, the WSE runtime chunks the outgoing DIME records so that the record size of the object being streamed doesn t need to be defined. The last record chunk is written when the end of the stream is reached. The following sample takes an input stream from a file (such as a large binary image file), writes the stream to a series of chunked DIME records, and writes the DIME message to a Stream object, which might be a NetworkStream over TCP.

 // This sample transforms a binary input stream to a DIME stream 
// of specified MIME media-type.
static void WriteToDime(Stream inputStr,Stream dimeStr,
string mediaType)
{
// create writer for DIME message out to the dimeStream
DimeWriter myDW = new DimeWriter(dimeStr);

// Create a GUID for the DIME record ID
Guid guid = Guid.NewGuid();
string id = string.Format(uuid:{0}", guid.ToString());

// Create a new DIME record where the MIME media type is specified
// by mediaType and contentLength of -1 to specify chunking
DimeRecord myRecord = myDW.LastRecord(id, mediaType,
TypeFormatEnum.MediaType, -1);

// Set a rather small size for each record chunk at 4KB
myRecord.ChunkSize = 4096;

// Use BinaryWriter to write a stream to the DimeRecord
BinaryWriter myWriter = new BinaryWriter(myRecord.BodyStream);

// Write bytes from incoming stream to BinaryWriter
int size = 4096;
byte[] bytes = new byte[4096];
int numBytes;
while((numBytes = inputStr.Read(bytes, 0, size)) > 0)
{
myWriter.Write(bytes, 0, numBytes);
}

// Cleanup
myDW.Close();
}

Notice that if you use an HTTP-based implementation of DIME, the entire DIME message gets read into memory; but when streaming directly to DIME, you can use DimeRecord.ChunkSize to limit the amount of memory that you need to use.

The following method implements the opposite procedure, namely reading a DIME stream and extracting the record contents to a binary stream:

 static void ReadFromDime(Stream dimeStr, Stream outStr) 
{
// Create a reader for the streamed DIME message.
DimeReader myDR = new DimeReader(dimeStr);

// Create a DimeRecord to read the current (and only) DIME record
DimeRecord myRecord = myDR.ReadRecord();

// Create a reader to read the record contents into
BinaryReader myReader = new BinaryReader(myRecord.BodyStream);

// Write bytes to the output stream
int size = 4096;
byte[] bytes = new byte[4096];
int numBytes;
while((numBytes = myReader.Read(bytes, 0, size)) > 0)
{
outStr.Write(bytes, 0, numBytes);
}

// cleanup
myReader.Close();
myDR.Close();
}

Limitations of Attachment Support in WSE

As with any new product, there are some limitations in how WSE supports DIME, WS-Attachment, and related specifications. These issues are discussed in the following sections.

Referencing Attachments

WSE supports the ID field for DIME records, which can be set using the DimeAttachment.Id property. This property, which maps to the ID field of the DIME record, lets you assign and access attachments using friendly ID-string values rather than integer-index values. For example, you can access an attachment with the ID Tom simply by specifying [ Tom ] instead of the array index [0], as follows:

Note

Technically, ID should be a URI, but this requirement isn t strictly enforced by WSE.

The WS-Attachment specification prescribes that attachments be referenced from the primary SOAP message by the ID values of the DIME record in which they are contained. This is to improve performance of DIME parsers by enabling them to read the primary SOAP message and then quickly locate an attachment by parsing only record headers until the desired record is found. However, in this version of WSE, Microsoft has taken a more autonomous view of attachments, which means that the WSE runtime merely extracts each attachment in the order received and keeps each as an object in memory to be accessed as needed.

No WSDL Support for DIME

The WS-Attachments specification details a method for supporting DIME in the Web Services Description Language (WSDL). WSE doesn t include any updates of the WSDL generation functionality in ASP.NET or of the Wsdl.exe tool. This means that to publish the fact that a Web service supports DIME, you ll have to manually edit and publish the WSDL file according to this new specification. However, although describing the DIME functionality in WSDL is the most correct way to go, full description isn t really necessary since you can easily check the SoapContext object on incoming messages to see whether any attachments were included.

Security

While WSE provides comprehensive support for securing SOAP messages according to the WS-Security specifications, this support doesn t extend to attachments to SOAP messages sent in a DIME message. When you tell WSE to sign and encrypt the primary SOAP message, the SOAP message passes through the WSE SecurityOutputFilter , where it gets appropriately secured. Only after passing through the various filters does the SOAP message get encapsulated as a DIME message with the attachments, which means that the attachments themselves aren t secured in any way since they can be read by any DIME parser.

For the most part, the WSE implementation is limited by the fundamental security problems with DIME and WS-Attachments that I described earlier. In addition, WSE doesn t include any built-in mechanism for either encrypting the DIME message or referencing any encrypted part in the Security header as described by the WS-Security specification. Therefore, if you need to secure the attachments in DIME messages, you should use one of the .NET Framework “ supported encryption mechanisms provided by the System.Security.Cryptography namespace. Again, since the DIME specification doesn t define a method for digitally signing DIME records, WSE has no way to determine whether DIME messages have been tampered with. Therefore, even if you have secured the attachment, you should always use a secured transport when transmitting sensitive data in a DIME message.




Understanding Web Services Specifications and the WSE
Understanding Web Services Specifications and the WSE (Pro Developer)
ISBN: 0735619131
EAN: 2147483647
Year: 2006
Pages: 79

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net