Attach Docs to SOAP Messages

Send binary data from Web services with the WSE.

XtremeData

LANGUAGES: C#

TECHNOLOGIES: WSE | SOAP | DIME | GDI+

 

Attach Docs to SOAP Messages

Send binary data from Web services with the WSE.

 

By Dan Wahlin

 

Web services are constantly evolving to support more and more advanced requirements. Fortunately, new tools are emerging to help simplify the development of Web services and make them more efficient in the process. In this article, I'll introduce you to one new and exciting Web service technology relating to a specification referred to as WS-Attachments.

 

Consider a Web service that returns a binary image, such as a JPEG. Because the returned data is binary, it doesn't make much sense to "serialize" it into a SOAP message. The WS-Attachments specification was created for just that reason. Supported by Microsoft's Web Service Enhancements (WSE), the WS-Attachments specification instead allows a SOAP message to reference an image (or other document). This reduces the processor's overhead because it doesn't have to convert binary data to encoded bytes, thus minimizing the size of the SOAP messages. (For more on WSE, see the sidebar, "Web Service Enhancements.")

 

In a nutshell, the WS-Attachments specification defines how you can "attach" documents and files to a SOAP message as opposed to embedding them directly within the body of one. This is possible by encapsulating the SOAP message and document using a technology named Direct Internet Message Encapsulation (DIME).

 

Build a Charting Web Service

To see WS-Attachments and the WSE in action, let's take a look at how to create a Web service capable of generating on-the-fly bar and line charts. This service accepts an XML document containing data points to add to the chart and returns a png image as a SOAP attachment (the service also could return other image types). Figure 1 shows an example of a bar chart returned from the charting Web service.

 


Figure 1. The charting Web service parses XML data and is used along with GDI+ classes in .NET to create a bar chart.

 

The XML document that was sent to the Web service and used to create the bar chart in Figure 1 is shown in Figure 2.You'll notice by looking through the different elements and attributes that the document marks up point data the Web service should graph.

 

    width="500" graphTitle="Golf Scores">

    

    

    

    

    

    

    

Figure 2. This simple XML document defines the size of the image, what type of chart should be generated, and the data to be graphed. The client sends the XML in a SOAP message to the charting Web service.

 

The charting Web service relies on a custom C# class named ChartGenerator to parse the XML shown in Figure 2. ChartGenerator has several different methods that handle extracting data from the XML document and drawing lines, text, and individual bars. Figure 3 shows ChartGenerator's only public method, GenerateChart (you can download the entire ChartGenerator class).

 

public Stream GenerateChart() {

    //Get height and width of chart

    XmlElement root = (XmlElement)this.DataSource;

    _ChartSize =

      new SizeF(float.Parse(root.GetAttribute("width")),

      float.Parse(root.GetAttribute("height")));

    int totalPoints = 0;

 

    //Find maximum value of XML chart data

    this._MaxValue = GetMaxValue(out totalPoints);

    //Find number of points in XML chart data

    this._TotalPoints = totalPoints;

 

    //Create a new Bitmap object

    Bitmap b =

      new Bitmap((int)_ChartSize.Width,

       (int)_ChartSize.Height,

      PixelFormat.Format32bppArgb);

    //Create a graphics drawing surface

    _Graphics = Graphics.FromImage(b);

 

    //Set background color to white

    _Graphics.Clear(Color.White);

    _Graphics.SmoothingMode = SmoothingMode.AntiAlias;

    _Graphics.TextRenderingHint =

      TextRenderingHint.AntiAlias;

    //Delegate responsibility for drawing lines,

    //text, and bars to the class's private methods

    DrawLines();

    DrawText();

    DrawBars();

 

    //Return a stream containing the image data

    //to the caller of the method

    MemoryStream s = new MemoryStream();

    b.Save(s,ImageFormat.Png);

    return s;

}

Figure 3. The ChartGenerator class has several methods that draw the chart image. The Web service calls the GenerateChart() method to handle the drawing of the chart.

 

GenerateChart creates the initial Bitmap object that will be drawn upon by reading height and width data from the XML document shown earlier. It also discovers the maximum value of the XML data points, determines the total number of data points, and creates a Graphics object used to draw upon the Bitmap. Once these activities are complete, lines, text, and bars are drawn by calling three private methods: DrawLines, DrawText, and DrawBars.

 

After the image is complete, the GenerateChart method returns it as a MemoryStream object. The image could be saved to disk instead of being returned as a stream, but by using a stream, the application will perform more efficiently because the file system won't have to be touched. Currently, the GenerateChart method returns a png image format; the code easily can be modified, however, to return other formats such as JPEG or BMP.

 

Add DIME to Web Methods

Now that you're familiar with the ChartGenerator class and its GenerateChart method, let's see how to call it and attach the returned image stream to a SOAP message using the WSE. The first task is to modify the web.config file. To integrate the WSE classes into the SOAP processing pipeline, you must add the configuration code in Figure 4 to web.config, between the start and end tags. Unless you add this configuration code, the classes that handle document attachments will not work properly.

 

  

        

    

      priority="1" group="0"/>

  

Figure 4. Although installing the WSE provides you with access to many different extension classes, different types of configuration entries must be added into the web.config file for these classes to be used within Web services. In the case of WS-Attachments functionality, the XML configuration code shown here must be added within the start and close tags.

 

After modifying web.config, the Microsoft.Web.Services and Microsoft.Web.Services.Dime namespaces must be imported into the chart Web service to use the WS-Attachments and DIME classes. A class named DimeAttachment contains DIME-specific functionality. You can create a DimeAttachment instance by using one of several overloaded constructors (see Figure 5).

 

public DimeAttachment();

 

public DimeAttachment(System.String,

    TypeFormatEnum, System.IO.Stream);

 

public DimeAttachment(System.String,

    TypeFormatEnum, System.String);

 

public DimeAttachment(System.String,

    System.String, TypeFormatEnum, System.IO.Stream);

 

public DimeAttachment(System.String,

    System.String, TypeFormatEnum, System.String);

Figure 5. The DimeAttachment class has several overloaded constructors you can use to create an instance of the class. The path to the document to attach a stream containing the document data can be passed to the constructor. See the WSE documentation for more details about the constructor parameters.

 

Each constructor allows you to identify the type of payload (i.e., "image/jpeg" or "image/png") being loaded as an attachment as well as its format. An enumeration named TypeFormatEnum represents the format, and it can have a value of AbsoluteUri, MediaType, None, Unchanged, or Unknown. This article's charting Web service uses a TypeFormatEnum value of MediaType. Aside from these parameters, you can load the source document to be attached by passing in the path to the document or the stream that represents the document.

 

Associating a DimeAttachment class with a SOAP message requires access to the current SOAP context. Because the charting Web service sends an attachment along with the SOAP Response message, the Response context must be accessed. You do this by calling the HttpSoapContext object's ResponseContext property (see Figure 6).

 

[WebMethod]

public bool GenerateChartImage(XmlNode data) {

  bool status = true;

  try {

    //Create instance of charting class

    ChartGenerator gen = new ChartGenerator();

    //Assign XML data points to DataSource property

    gen.DataSource = data;

    //Generate the chart image

    Stream s = gen.GenerateChart();

 

    //Access the SOAP Response context

    SoapContext resp = HttpSoapContext.ResponseContext;

    //Attach the chart image (DIME encapsulation)

    resp.Attachments.Add(

      new DimeAttachment("image/png",

        TypeFormatEnum.MediaType,s));

    }

  catch (Exception exp) {

    status = false;

  }

  return status;

}

Figure 6. The GenerateChartImage() Web Method creates an instance of the ChartGenerator class, assigning the chart's data source, then calls its GenerateChart() method.

 

The GenerateChartImage Web Method calls the ChartGenerator object and adds the returned stream into a DimeAttachment object. Then, this attachment is added to the current Response SOAP context's Attachments collection. Note that, with only a few lines of code, you can add a document as a SOAP attachment. Very nice!

 

Access SOAP Attachments

The client of the charting Web service must perform three tasks to generate and return the chart. First, it must create an XML document containing chart data points (refer to the XML document shown in Figure 2). Next, it must call the Web service's GenerateChartImage Web Method and pass the XML document as a parameter. This call is made through a Web service proxy object. Note that the client proxy class must inherit from the Microsoft.Web.Services.WebServicesClientProtocol class rather than the normal System.Web.Services.Protocols.SoapHttpClientProtocol class to use the WSE classes. This change requires you to edit the proxy code manually. Finally, the client must access the Response SOAP context, extract the image attachment, and either save it to the file system or write it to the Response stream. Figure 7 demonstrates how to perform these steps. Several comments have been added to help explain what the code is doing at each step.

 

Bitmap b = null;

XmlDocument xmlDoc = new XmlDocument();

 

//1. XML loaded from text area. It could also be loaded

//from a file or created dynamically.

xmlDoc.LoadXml(this.txtChartData.Text);

 

//2. Call the Web Service through the proxy

ChartService.ChartServiceWebServiceProxy.ChartService

  proxy = new ChartService.ChartServiceWebServiceProxy.

  ChartService();

proxy.GenerateChartImage(xmlDoc);

 

//3. Ensure that an attachment was received and save it

//either to a file or to the Response output stream

if (proxy.ResponseSoapContext.Attachments.Count > 0) {

  //Load chart image attachment into Bitmap

  b = new Bitmap(

  proxy.ResponseSoapContext.Attachments[0].Stream);

  MemoryStream s = new MemoryStream();

  //Save to memory stream...necessary for png images

  b.Save(s,ImageFormat.Png);

  //Set content type

  Response.ContentType = "image/png";

  //Save stream data to the Response stream

  s.WriteTo(Response.OutputStream);

}

Figure 7. The Web service's client does not need to install any charting software to generate chart images. The client must have the WSE classes installed, however, to use WS-Attachments/DIME functionality.

 

Figures 8 and 9 show additional types of chart images you can generate by passing different XML data to the charting Web service.

 


Figure 8. This image shows how to overlay a line chart on a bar chart. You accomplish this by setting the barChart and lineChart attribute values to True in the XML document that is sent to the Web service.

 


Figure 9. You can generate line charts using the charting Web service by setting the barChart attribute in the source XML document to False and the lineChart attribute to True.

 

The WSE makes implementing the WS-Attachments specification extremely straightforward. As a result, different types of documents - from images to PDFs to Word documents - can be exchanged between entities in a much more efficient manner compared to "serializing" the documents into the SOAP message. This technology opens up a whole new world for Web services because clients with WS-Attachments capabilities can hit services that return a wide variety of complex data without installing specialized software.

 

You can run a live example of the charting Web service at the following URL on the XML for ASP.NET Developers Web site: http://www.XMLforASP.NET/codeSection.aspx?csID=96.

 

The sample code referenced in this article is available for download.

 

Dan Wahlin (a Microsoft Most Valuable Professional in ASP.NET) is the president of Wahlin Consulting and founded the XML for ASP.NET Developers Web site (http://www.XMLforASP.NET), which focuses on using XML and Web Services in Microsoft's .NET platform. He also is a corporate trainer and speaker, and he teaches XML and ASP.NET training courses around the United States. Dan co-authored Professional Windows DNA (Wrox) and ASP.NET Tips, Tutorials & Code (Sams), and he authored XML for ASP.NET Developers (Sams). E-mail Dan at [email protected].

 

Learn More About DIME

You can think of DIME as a wrapper around a SOAP message and an associated document or file.

For more information, you can find an article on the subject at http://msdn.microsoft.com/webservices/default.aspx?pull=/library/en-us/dnwebsrv/html/DIMEWSAttch.asp. You also can read the full WS-Attachments specification at http://msdn.microsoft.com/webservices/understanding/gxa/default.aspx?pull=/library/en-us/dnglobspec/html/wsattachmentsindex.asp.

 

Web Service Enhancements

Web Services started out as a very simple concept: Exchange data between two or more entities, using XML messages. With the release of development platforms such as Microsoft's .NET Framework, this initial concept has become useful in many different scenarios.

 

Because of the growth of the Web Services technology and its rapid proliferation, though, it has been in need of new and improved features to handle a variety of tasks such as securing messages and authenticating Web service callers. To accommodate this need, Microsoft has released Web Service Enhancements (WSE), which lets you integrate such features into Web services with only a minimal investment of time and effort.

 

In this article, I've dealt only with one specific area of WSE - the new WS-Attachments specification - but Web Services has experienced growth pains in other areas, as well. If you're interested in learning more about other WSE features that help address these growth pains, such as authenticating Web service callers and encrypting SOAP messages, see Practice Safe Web Services and Protect Your Info.

 

Tell us what you think! Please send any comments about this article to [email protected]. Please include the article title and author.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish