Consuming RESTful services from BizTalk Server using AtomPub

In a previous post I explained the importance for traditional integration servers and ESBs to embrace REST as one of the architecture styles used to interact and expose resources. For the simplicity of this post (and to avoid starting a religious war J) I am going to use the term ESB as the generic name to refer to integration and mediation technologies that can interact with RESTful services. So yes, BizTalk Server is going to be an ESB for the next few paragraphs J

Erroneously, a lot of developers tend to think that the only technologies required to interact with RESTful service are HTTP together with a decent support for XML and JavaScript. This is partially based on the over marketed "simplicity of REST" that a lot of people have used as a flag against alternatives such as SOAP and WS-* protocols. If that was the case, I should probably be writing about something else because, as we all know, HTTP and XML has been ubiquitously adopted by all the ESBs in the market. The reality is that  interacting with RESTful services typically entails an extra set of considerations such as supporting resource-centric protocols such as Atom Publishing Protocol (AtomPub), Resource Definition Framework (RDF), JSON or Web3S as well as low level manipulation of the different components of the HTTP protocol. Factoring all that, we can find that achieving a seamless interaction with RESTful services from still requires a significant amount of effort. The good news is that a lot of the ESBs in the market provide the necessary infrastructure to achieve a decent level of interaction with RESTful services. On this post I would like to explore some of the techniques available in BizTalk Server 2006 and 2006-R2 to interact with RESTful services using AtomPub.

AtomPub

Atom Publishing Protocol (AtomPub) has become one of the most popular protocols in the RESTful services ecosystems. AtomPub is an HTTP-based approach for creating and editing Web resources. It is designed fundamentally around the idea of using the basic operations provided by the HTTP protocol (such as GET, PUT, and DELETE) to pass around instances of Atom  Feed and Entry documents that represent things like blog entries, podcasts, wiki pages, calendar entries and so on. Although BizTalk Server provides a very strong support for HTTP and XML it does not natively implements AtomPub.  However other Microsoft technologies such as Windows Live services and ADO.NET Data Services have recently embraced AtomPub as a resource-based interaction protocol. Specifically, ADO.NET Data Services leverages AtomPub as one of the mechanisms for interacting with relational databases using RESTful services. If you are interested on learning more about ADO.NET Data Service you should check out the documentation together with the ADO.NET Data Services blog.

Show me the code

Let's start with the following ADO.NET Entity Model that express the relationship between contacts and accounts.

Figure 1: ADO.NET entity model

Using ADO.NET Data Services we can generate the following service that exposes RESTful interfaces based on the previous ADO.NET Entity Model. For this sample we can use the default configuration generated by the ADO.NET Data Service project template.

[System.ServiceModel.Activation.AspNetCompatibilityRequirements(RequirementsMode= System.ServiceModel.Activation.AspNetCompatibilityRequirementsMode.Required)]

public class acservice : WebDataService<ACCModel.ACCEntities>

{

    // This method is called once during service initialization to allow

    // service-specific policies to be set

    public static void InitializeService(IWebDataServiceConfiguration config)

    {

       config.SetResourceContainerAccessRule("*", ResourceContainerRights.All);

       config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);

    }

}

Figure 2: ADO.NET Data Service

In order to create a new Contact we need to create the following AtomPub message and send it as part of an HTTP POST.

------HTTP Headers---------

Content-Type:application/atom+xml;type=entry

------HTTP Body-----------

<entry xmlns:ads="http://schemas.microsoft.com/ado/2007/08/dataweb" xmlns="http://www.w3.org/2005/Atom">

<content type="application/xml">

      <ads:id>1</ads:id>

      <ads:FirstName>John</ads:FirstName>

      <ads:LastName>Doe</ads:LastName>

    </content>

</entry>

Figure 3: Sample AtomPub request

Notice that, additionally to the AtomPub message, the Content-Type HTTP header must be set to application/atomxml;type=entry.

BizTalk Server and AtomPub

Lets the fun begin J.... Suppose that we would like to consume the service created in the previous section from BizTalk Server. Obviously, the HTTP adapter is the natural choice for this scenario especially given that BizTalk Server R2 still does not support the WebHttpBinding included in the .NET Framework 3.5. However given that BizTalk relies on a completely strongly typed programming model (a.k.a XML Schemas must be accessible at design time) we need to have the AtomPub Xml Schema definition. Well, it turns out that AtomPub has no "official" Xml Schema model. Instead, the authors chose RelaxNG as the language to model the AtomPub specification. Given that RelaxNG is a very flexible language and easily translatable to Xml Schema, I've use a version of this converter (http://www.thaiopensource.com/relaxng/trang.html) to create an Xml Schema representation of AtomPub. Also for the simplicity of the example, and given that complex schemas are not the best thing for BizTalk performance wise, I removed the elements that we were not using as part of this example. The BizTalk project containing the AtomPub artifacts can be downloaded as part of the samples.

<xs:schema xmlns:atom="http://www.w3.org/2005/Atom" xmlns:b="http://schemas.microsoft.com/BizTalk/2003" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.w3.org/2005/Atom" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:import schemaLocation="adsm.xsd" namespace="http://schemas.microsoft.com/ado/2007/08/dataweb/metadata" />

  <xs:import schemaLocation="xml.xsd" namespace="http://www.w3.org/XML/1998/namespace" />

  <xs:annotation>

    <xs:appinfo>

      <references xmlns="http://schemas.microsoft.com/BizTalk/2003">

        <reference targetNamespace="http://www.w3.org/XML/1998/namespace" />

        <reference targetNamespace="http://schemas.microsoft.com/ado/2007/08/dataweb/metadata" />

      </references>

    </xs:appinfo>

  </xs:annotation>

  <xs:element name="feed" type="atom:feedType" />

  <xs:element name="entry" type="atom:entryType" />

 

  Rest of the schema....

</xs:schema>

Figure 4: AtomPub Xml Schema fragment

In addition to the AtomPub Xml Schema we also need the XSD of the specific resource we would like to create. In this case, are going to use the following XSD that describes the contact resource.

<xs:schema xmlns:b="http://schemas.microsoft.com/BizTalk/2003" xmlns="http://schemas.microsoft.com/ado/2007/08/dataweb" targetNamespace="http://schemas.microsoft.com/ado/2007/08/dataweb" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="Contact">

    <xs:complexType>

      <xs:sequence>

        <xs:element form="qualified" name="FirstName" type="xs:string" />

        <xs:element form="qualified" name="LastName" type="xs:string" />

      </xs:sequence>

    </xs:complexType>

  </xs:element>

</xs:schema>

Figure 5: Sample Xml Schema

As per the AtomPub specification, the contacts xml instance gets serialized as an Atom entry in the AtomPub message. Although there are several ways to achieve that on BizTalk Server, I decided to take advantage of the web syndication programming model included as part of the .NET Framework 3.5 to implement the serialization mechanisms that creates the required AtomPub message. Specifically, the following helper class uses the AtomSerializer to create an AtomPub message that includes an entry based on a BizTalk XLANG message.

public class AtomPubHelper

    {

        public static XmlDocument CreateResourceAtomPubRequest(XLANGMessage message)

        {

                XmlReader reader= (XmlReader)message[0].RetrieveAs(typeof(XmlReader));

                if (reader.Read())

                {

                    XmlDocument atompubMessage = new XmlDocument();

                    atompubMessage.Load(SerializeAtomMessage(reader));

                    return atompubMessage;

                }

                else

                    return null;

        }

 

        private static Stream SerializeAtomMessage(XmlReader messageReader)

        {

            Stream stream = new MemoryStream();

            XmlWriter writer = XmlWriter.Create(stream);

            XmlSyndicationContent content = new XmlSyndicationContent(messageReader);

            SyndicationItem item = new SyndicationItem();

            item.Content = content;

            item.SaveAsAtom10(writer);

            writer.Flush();

            stream.Seek(0, SeekOrigin.Begin);

            return stream;

        }

    }

Figure 6: AtomPub serialization helper

With all the artifacts explained in this section, we can create the following orchestration that receives a contacts message, creates an AtomPub request with a contact entry and sends it to the ADO.NET Data Services RESTful service using an HTTP POST.

Figure 7: Client BizTalk orchestration

The message assignment shape utilizes the AtomPub helper class explained previously in order to create the AtomPub message.

AtomPubRequest= BizTalk.Samples.AtomPubUtils.AtomPubHelper.CreateResourceAtomPubRequest(InMsg);

The solicit-response HTTP port configuration looks like the following. Notice that the Content-Type property is set to application/atom+xml;type=entry as required on the AtomPub specification.

Figure 8: HTTP configuration port

Running the following message through the BizTalk orchestration

<ns0:Contact xmlns:ns0="http://schemas.microsoft.com/ado/2007/08/dataweb">

  <ns0:FirstName>FirstName_0</ns0:FirstName>

  <ns0:LastName>LastName_0</ns0:LastName>

</ns0:Contact>

Figure 9: Sample input message

Produces the following AtomPub request and response messages.

<entry xmlns="http://www.w3.org/2005/Atom">

  <id>uuid:f6bddb55-623f-412b-95b7-66f9d9d69f04;id=1</id>

  <title type="text">

  </title>

  <updated>2008-04-22T03:23:15Z</updated>

  <content type="">

    <ns0:FirstName xmlns:ns0="http://schemas.microsoft.com/ado/2007/08/dataweb">FirstName_0</ns0:FirstName>

    <ns0:LastName xmlns:ns0="http://schemas.microsoft.com/ado/2007/08/dataweb">LastName_0</ns0:LastName>

  </content>

</entry>

Figure 10: AtomPub request produced by BizTalk Server

<entry xml:base="http://localhost:8080/restsamples/acservice-ds.svc/" xmlns:ads="http://schemas.microsoft.com/ado/2007/08/dataweb" xmlns:adsm="http://schemas.microsoft.com/ado/2007/08/dataweb/metadata" adsm:type="ACCModel.Contact" xmlns="http://www.w3.org/2005/Atom">

  <id>http://localhost:8080/restsamples/acservice-ds.svc/Contact(0)</id>

  <updated />

  <title />

  <author>

    <name />

  </author>

  <link rel="edit" href="Contact(0)" title="Contact" />

  <content type="application/xml">

    <ads:id adsm:type="Int32">0</ads:id>

    <ads:FirstName>FirstName_0</ads:FirstName>

    <ads:LastName>LastName_0</ads:LastName>

  </content>

  <link rel="related" title="Account" href="Contact(0)/Account" type="application/atom+xml;type=entry" />

</entry>

Figure 11: AtomPub response

The sample code for this example can be downloaded here.

4 Comments

  • This works for creating a new REST entry on the service. Thansk for sharing. However, what would be your strategy to handle an AtomPub entry read (GET) from BizTalk?

  • Doesn't work for me. My configuration is biztalk server 2009 and I use the framework 3.5. Does anybody has an idea?

  • Anyway you can update this post for WCF Data Services? I have been trying to get this to work and I am failing miserably. The Rest Service I have set up for the Contact keeps rejecting the message with error code 500. Basically it says LastName is null which fails on insert. This is happening because the SaveAsAtom10 is not producing


    Name


    Where as your code produces <ns0: ..... within the content. At least this why I think it fails. Anyideas?

    I was able to watch this fail with Fiddler.

  • Bottom line the missing part in the sample that is broken for me is how the Message gets transformed from AtomPubRequest to AtomPubClient.atom.entry .

Comments have been disabled for this content.