| |
| 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. |
| |
| |