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.