Click Here to Install Silverlight*
IndiaChange|All Microsoft Sites
MSDN
|Developer Centers|Library|Downloads|How To Buy|Subscribers|My MSDN
 
WS-Attachments
By Dhananjay Katre
 
Article Posted: July 29, 2003
 
Introduction
 
Web Service Enhancements 1.0 (referred henceforth as WSE) for the Microsoft .NET Framework allow Web Service developers to code advanced functionalities in web services such as providing security to web services, specify routing information, adding attachment files to SOAP messages and so on. It allows to specify security information by allowing developers to add security credentials, allow digital signing of messages and allowing encryption of the complete SOAP message or part of it. In this article we will specifically concentrate on showing how WS-Attachments specification (used in WSE) can be used for handling attachments in SOAP.
 
This article will give an example of two web services that are used to upload image files onto the server. The first example web service would provide for uploading an attachment without using WS-Attachments. The second web service, will use the WS-Attachment features to handle attachments in SOAP messages. Using this approach, the reader will be able to see the ease with which web services can handle attachments using WS-Attachments and the advantages gained by using the attachment features.
 
We will see how to create web services to upload image files on to the server. The client for these web services would be a common windows application. This client would have options for uploading the image files onto the server. During this process, all the SOAP messages that are being sent by the client to the two different web services would be intercepted and written to a file. By looking at these SOAP messages, the reader would come to know how attachments are handled by the web services with and without using WS-Attachment features.
 
 
What are WS-Attachments?
 
Before we understand WS-Attachments, let us just take a moment to understand how web services function with respect to method calls and responses. As the readers are aware, the basic data formatting for sending request to a web service method and receiving the response back is the SOAP format.
 
SOAP is but an implementation of XML that conforms to the SOAP specifications. Thus all the parameters of the request sent to the public web service methods are serialized into XML form. This XML becomes part of the SOAP request that the client sends to the public web service method. Similarly when the public web service method returns a response back to the client in a SOAP response, the result also needs to be serialized into XML. Since XML serialization is needed for this to work, only objects that support serialization are eligible to become parameters to the public web service methods and only serializable objects can be returned as responses from the public web service methods.
 
Now we are thinking of creating a web service that exposes a method for accepting attachment files from a client of the web service. Since communication from the client would be in SOAP format, the attachment file needs to be serialized into XML. When large binary files are serialized into XML, the resulting XML size can be very huge as compared to the original size of the attached file. As a result when the client invokes the upload web service with large binary files, the SOAP requests generated by the client would have a very large size. Is there an better way in which we can handle sending of attachments in a SOAP message. This is where the WS-Attachments specifications help.
 
The WS-Attachments specifications allow files to be added to the SOAP message without being serialized into XML!!! You may ask how is this possible? Since SOAP is based on XML and if we want to send some information in the SOAP message, it has to follow the normal rules of XML. The answer would be to send the SOAP message, but keep the attachment file data outside the SOAP message. As a result since, the data is not part of the SOAP envelope, it does not need to be XML.
 
The concept of WS-Attachments is based on DIME specifications. DIME stands for Direct Internet Message Encapsulation. It is a lightweight, binary message format, for the encapsulation of application-defined payloads into a single message construct. A DIME message is a general encapsulation format that contains one or more DIME records. Each record can contain an application payload of arbitrary type and size. These application payloads can be obviously binary files and any message constructs. Note also that these application-defined payloads need not be serialized into XML. The only parameters described by DIME is the payload type, the length, and an optional payload identifier.
 
The WS-Attachments specifications defines a model for handling SOAP messages with attachments. It uses the DIME specifications to send the SOAP message and the attachments as DIME records in a DIME message. This encapsulation is based on a compound SOAP structure that consists of a primary part containing the SOAP message and zero or more secondary parts containing the attachments. The first DIME record in the DIME message would be the primary part containing the SOAP message and the subsequent parts need to be attachments or other application payloads.
 
WSE provides an implementation of the WS-Attachment features which allows developers to incorporate the attachment facilities into the web services. Now to really understand the benefits offered by the WS-Attachments specifications, we will be creating two web services. One will be used for uploading an image file without using WS-Attachments and the second web service will be used for uploading the same file, but with using WS-Attachments. Both of these web services would be created in one project named Images since we are going to deal with image files.
 
 
Upload Web Service
 
First we will create a simple web service named UploadService that exposes a method called UploadFile(). In our example we want to restrict the upload only to image files. If we think of what should be the parameters to the method we can say that an Image file can be a parameter to the method. Note however, that we had earlier mentioned that all the parameters of the public web services methods must be serializable to XML. Image object cannot be serialized to XML. Hence we will design our method to accept two parameters, one being the name of the file in string format and the second parameter would be an array of bytes that contains the actual data i.e. the binary data of the attachment in a byte array.
 
The code below illustrates the UploadFile() method of the web service.
 
 [WebMethod]
 public string UploadFile(string sFileName, byte[] data)
 {
  if ((sFileName == null) || ("".Equals(sFileName)))
   {
      return ("No file specified");
   }

   try
     {
       string sPath = "C:\\Images\\";
       Stream oStream = new FileStream(sPath + sFileName, FileMode.Create,        FileAccess.Write);
       oStream.Write(data, 0, data.Length);
       oStream.Flush();
       oStream.Close();
       return "File uploaded successfully";
     }
   catch (Exception e)
     {
      return e.Message.ToString();
     }
  }

 
This code first checks that the file name is null or an empty string. Only if the file name contains something valid, it will proceed ahead. It then creates a new file stream and writes the data to a file with the name specified in the parameter sFileName to the directory C:\Images. Before we create a client application to consume this web service, we will also create the second web service that uses WS-Attachments in the same project.
 
 
Upload Web Service using WS-Attachments
 
We will develop another web service that provides the same functionality as the web service that we developed in the earlier section. Only this time around we will use WS-Attachments feature of WSE.
 
To start using the WSE in your code, you must first change the Web.Config file to use the Web Service extension types as shown below.
 <webServices>
   <soapExtensionTypes>
     <add type="Microsoft.Web.Services.WebServicesExtension,
          Microsoft.Web.Services,Version=1.0.0.0,Culture=neutral,
          PublicKeyToken=31bf3856ad364e35" priority="1" group="0"
     />
   </soapExtensionTypes>
 </webServices>

The code for the UploadServiceWSE web service is as shown below. You need to import the following assemblies in the code.

 using System.Web.Services;
 using Microsoft.Web.Services;
 using System.Drawing;
 using System.Drawing.Imaging;

 
The actual web service method that receives the attachments and then stores the received file to a directory on the server is as shown below. The exposed Web Service method has just a single parameter that receives the name of the file that will be used for storing the file. Note that the data (file data) is not sent as a parameter of the exposed web service method as in the previous web service example.
 
The web service first checks if the file name is not null or an empty string. It then obtains the SOAP context object SoapContext from the request that the client has sent. The SoapContext object contains a property called Attachments. The Attachments property contains the list of all the attachments with the given SOAP message. In our code sample, we are just accessing the first element of the Attachments property, since our example web service only expects one image file to be uploaded at a time. Once we get the image, we save it to the C:\Images drive on the server in Jpeg format.
 
 [WebMethod]
 public string UploadFile(string sFileName)
 {

  if ((sFileName == null) || ("".Equals(sFileName)))
   {
    return ("No file specified");
   }

  try
   {
    SoapContext oRequestContext = HttpSoapContext.RequestContext;
    Image oImage = new Bitmap(oRequestContext.Attachments[0].Stream);
    oImage.Save("C:\\Images\\" + sFileName, ImageFormat.Jpeg);
    return "File Uploaded Successfully";
   }
  catch (Exception e)
   {
    return e.Message.ToString();
   }
 }

 
In the example Web Service, we are just accepting one file as an attachment and also storing it as JPEG format. I real life Web Services of this kind, the Web Service would check the format of the uploaded file and save it accordingly.
 
Intercepting the SOAP Request
 
To have a better understanding of how the web services that we created above receive the web service requests in SOAP format, we will trap the SOAP requests and then write them to a file. We will be creating two files, the first file called Request.txt that would contain the SOAP message when the method UploadFile() of the web service UploadService is invoked. The second file will be RequestWSE.txt that would contain the SOAP message when the method UploadFile() of the web service UploadServiceWSE is invoked.
 
Fortunately Microsoft .NET Framework provides classes and interfaces that make all this possible. We can trap the request by coding a Http Module class. The requirement is that the class has to implement the IHttpModule interface. We save the request information in the PreRequestHandlerExecute event.
 
using System;
using System.Web;
using System.Reflection;
using System.Web.Caching;
using System.Web.Services.Protocols;
using System.IO;
using System.Text;

namespace Images
    {
    public class CInterceptor: IHttpModule
    {
        public CInterceptor()
    {
    }

    public void Init(HttpApplication httpApp)
    {
    httpApp.PreRequestHandlerExecute +=
    new EventHandler(this.onPreRequestHandlerExecute);
    }

    public void Dispose()
    {
    }

    public void onPreRequestHandlerExecute(object o, EventArgs ea)
    {
    HttpApplication httpApp = (HttpApplication) o;
    if (httpApp.Request.Url.ToString().Equals
    ("http://localhost/Images/UploadService.asmx"))
    {
    httpApp.Request.SaveAs("C:\\SOAPRequest.txt", false);
    }
    else if (httpApp.Request.Url.ToString().Equals
    ("http://localhost/Images/UploadServiceWSE.asmx"))
    {
    httpApp.Request.SaveAs("C:\\SOAPRequestWSE.txt", false);
    }

  }
  }
}

 
Before you can use this Http handler in your code, you need to make some changes to the configuration file i.e. Web.Config file. Basically you need to add an httpModules section to the System.Web section in the Web.Config file.
 
<httpModules>
<add name="httpmod" type="Images.CInterceptor, Images " />
</httpModules>
 
In the httpModules section, the type Images.Cinterceptor refers to the actual class name. "Images" refer to the name of the assembly containing the type for the http module.
 
Now with this configuration file updated, whenever a client application invokes the UploadFile() method of the UploadService web service, a file called SOAPRequest.txt would be save to the C:\ directory. Similary, when the client invokes the UploadFile() method of the UploadServiceWSE web service, a file called SOAPRequestWSE.txt would be saved to the same directory.
 
You can take a look at the contents of the two files that are generated and take a look at the size of the files. This will give you a fair idea as to how the web service that uses WS-Attachments encapsulates the attachments and what are the size savings by avoiding to serialize the contents of the file to XML.
 
Now we will create a windows client for the above Web Services.
 
 
Creating the windows client application
 
To create a client to upload files, we will create a window application. Figure 1 below shows the UI for the client application. It has two sections, one for uploading the file using the UploadService web service and the other section to use the UploadServiceWSE web service.
 

Upload File Client Application
 
In the windows client program, the client can browse the various directories for JPEG files and then click on the upload button to upload the file to the server. In the client project, we add reference to the following assemblies.
 
using System.Web.Services;
using Microsoft.Web.Services;
using Microsoft.Web.Services.Dime;
 
Also in addition to these assemblies, we add a reference to the web service that we created previously. When the proxy class is created, by default it extends from the class SoapHttpClientProtocol that resides in the namespace System.Web.Services.Protocols.
 
For the UploadServiceWSE to take the advantage of the WS-Attachments, you need to change the proxy class so that it now extends from the class WebServicesClientProtocol that resides in the Microsoft.Web.Services namespace. Go to the solution explorer, and locate the proxy. By default the name of the proxy file would be Reference.cs. It would be located under Reference.Map. If you do not see this file, click on the button "Show All File" at the top of the solution explorer.
 
namespace UploadFileClient.localhost1
{
using System.Diagnostics;
using System.Xml.Serialization;
using System;
using System.Web.Services.Protocols;
using System.ComponentModel;
using System.Web.Services;


/// <remarks/>
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="UploadServiceWSESoap", Namespace="http://tempuri.org/")]
public class UploadServiceWSE : Microsoft.Web.Services.WebServicesClientProtocol
{
    //Complete implementation not shown here
}
}
 
Now you can use the proxy classes have been set up and you can use them in the windows form code as shown below. First you need to import the namespaces of the proxy classes as shown below.
 
 
using UploadFileClient.localhost;
using UploadFileClient.localhost1;
 
The code for the client is as shown below. The code for the "Browse" button opens up the FileDialog object. This must be added to the form at design time. In this code we are restricting the file types to jpeg files and also disabling multi select since we only want to upload one file at a time.
 
private void btnBrowse_Click(object sender, System.EventArgs e)
{
    openFileDialog1.Filter
    = "Image files (*.jpg;*.jpeg)|*.jpg;*.jpeg";

    openFileDialog1.Title
    = "Select file to upload";

    openFileDialog1.Multiselect = false;
    openFileDialog1.ShowDialog();
}

private void openFileDialog1_FileOk(object sender,
    System.ComponentModel.CancelEventArgs e)
{
    txtFile.Text = openFileDialog1.FileName.ToString();
}

 
This code is the same for both the browse buttons. However, we have used a second instance of the OpenFileDialog control for the sake of simplicity for the second browse button.
 
The main logic for the uploading the files is put under the click event of the two Upload buttons provided.
 
For sending the SOAP request to the web service without attachments i.e. the UploadService,we obtain the name of the file that has been selected by the user. Then we instantiate the proxy object of the web service. We also create a stream that reads the data from the file selected by the user. The stream of data is then read into an array of bytes. Finally the web service method UploadFile() is called that expects the name of the file to be used for storing on the server and the array of bytes sent by the client. The following is the code of the Upload button for the web service UploadService.
 
private void btnUpload_Click(object sender, System.EventArgs e)
{
if ((txtFile.Text == null) || ("".Equals(txtFile.Text)))
    {
    MessageBox.Show("No file has been selected");
    }
else
    {
    this.Cursor = Cursors.WaitCursor;
    UploadService oUploadService = new UploadService();
    FileInfo objFile = new FileInfo(txtFile.Text);
    string sName = objFile.Name;
    Stream oStream = new FileStream(sName, FileMode.Open,     FileAccess.Read);
    byte[] data = new byte[oStream.Length];
    oStream.Read(data, 0, (int) oStream.Length);
    MessageBox.Show(oUploadService.UploadFile(sName, data),     "File Upload Status");
    txtFile.Text = "";
    this.Cursor = Cursors.Default;
    }
}
 
Now execute client and check the file C:\Images\SOAPRequest.txt to verify that the image data has been serialized into XML and is part of the SOAP message.
 
Now let us see how the client needs to send the SOAP request to the web service with attachments. For this, first we instantiate the proxy object of the web service and then obtain the RequestSoapContext. In WSE, we normally add all security, attachment as well as routing information in the Soap context object.
 
In this example, we create an object of type DimeAttachment passing in the type of the file and the file name (including the complete path to the file). Then we add this object to the Attachments property of the RequestSoapContext.
 
private void btnUploadWSE_Click(object sender, System.EventArgs e)
{
if ((txtFileWSE.Text == null) || ("".Equals(txtFileWSE.Text)))
    {
    MessageBox.Show("No file has been selected");
    }
else
    {
    this.Cursor = Cursors.WaitCursor;
    UploadServiceWSE oUploadService = new UploadServiceWSE();
    FileInfo objFile = new FileInfo(txtFileWSE.Text);
    string sName = objFile.Name;
    SoapContext oRequestContext = oUploadService.RequestSoapContext;
    DimeAttachment dimeAttach = new DimeAttachment("image/jpeg",
    TypeFormatEnum.MediaType,sName);
    oRequestContext.Attachments.Add(dimeAttach);
    MessageBox.Show(oUploadService.UploadFile(sName), "File     Upload Status");
    txtFileWSE.Text = "";
    this.Cursor = Cursors.Default;
    }
}
 
When the client invokes the upload functionality, the SOAP request that the client sends to the Web Service contains a basic SOAP request and the image data is kept outside of the SOAP request and hence the image data does not get serialized to XML. We can trap the request information on the server by intercepting the code.
 
Now execute client and check the file C:\Images\SOAPRequestWSE.txt to verify that the image data is kept outside of the SOAP message.
 
Once you have executed the upload functionalities of both the web services, you can check the file sizes and notice how much savings you will get if you use WS-Attachments for handling the SOAP attachments.
 
I executed the client, I uploaded an image file of size (237 KB). When I used the UploadService, the file called SOAPRequest.txt that was generated was of size 324 KB. The image data was serialized into XML and was part of the SOAP request. I then uploaded the same image file using the UploadServiceWSE web service. The file SOAPRequestWSE.txt that got generated was of the size 238 KB. In this case, the image data was not serialized into XML and was kept outside the SOAP envelope.
 
 
Web Services Enhancements 2.0
 
Microsoft has just released the beta version of WSE 2.0. There are many new features added to the WSE 2.0. However, here I will list down the changes that have to be done in the web services and the web service clients to use the WS-Attachments.
 
As I had mentioned earlier, to use the WS-Attachments, we need to modify the Web.Config file. The changes that have to be done are as shown below.
 
<webServices>
<soapExtensionTypes>
    <add type="Microsoft.Web.Services.WebServicesExtension,
    Microsoft.Web.Services, Version=2.0.0.0,Culture=neutral,
    PublicKeyToken=31bf3856ad364e35" priority="1"     group="0" />

</soapExtensionTypes>
</webServices>
 
Apart from the change in the configuration file, the web service needs to be changed as follows. We obtain the SOAP context in WSE 1.0 as follows:
 
  SoapContext oRequestContext = HttpSoapContext.RequestContext;
 
This gets changed to the following:
 
  SoapContext oRequestContext = RequestSoapContext.Current;
 
The earlier method of accessing the SOAP context i.e. the HttpSoapContext provides only support for the HTTP protocol. Now the new way of accessing the SOAP context supports other protocols. You could even use the old way of accessing the SOAP context i.e. the project compiles fine, however it generates a warning that the using HttpSoapContext way is obsolete.
 
As in the earlier example, the client side proxy class has to derive from Microsoft.Web.Services.WebServicesClientProtocol. The client also has to access the SOAP context class and it does that as follows:
 
  SoapContext oRequestContext = oUploadService.RequestSoapContext;
 
Thus using WSE 2.0 does not alter the way we use WS-Attachments very radically.
 
 
Summary
 
In this article we have seen what is WS-Attachments feature provided by the Web Service Enhancements in Microsoft .NET Framework. The WS-Attachments feature is used for handling attachments in SOAP messages. It uses the DIME format for handling attachments. The advantage in using WS-Attachments would be that if large files are to be sent as attachments in SOAP messages, they need not be serialized into XML. This means significant savings in the size of the attachment file, since serialization is not involved.
 
We have validated if that is the case by constructing two web services. The first web service is used for uploading image files to the server without using WS-Attachments. The second web service is used for uploading image files to the server, but using WS-Attachments. We have intercepted the SOAP messages sent by the client to both the web services and saved these to make a comparison in the two SOAP requests. These files illustrate that when we use WS-Attachments, the image data is not serialized into XML and that it is kept outside the SOAP envelope and hence results in significant size saving.
 
 

©2009 Microsoft Corporation. All rights reserved. Contact Us |Terms of Use |Trademarks |Privacy Statement
Microsoft