This article presents a workable solution for consuming ReSTful services using BizTalk Server 2010 .
We in the BizTalk Server CCxG team gratefully acknowledge the contributions of the following individuals for providing their valuable inputs in setting up the end-to-end scenario used in this article:
ReST stands for Representational State Transfer and defines an architecture to create network applications, typically over the World Wide Web. In most cases, the protocol used for a ReST client-server communication is HTTP. An application that uses ReST architecture is called a ReSTful application. Such applications use HTTP requests to create, update, read, and delete data, thereby supporting all CRUD operations. These CRUD operations are performed by ReST by supporting the GET, PUT, POST, and DELETE methods.
The two fundamental aspects of a ReST design pattern are:
Let us understand this with an example. An organization "Contoso" has a significantly large customer base and wants to provide customers with the option to view their details as stored in Contoso's records. If Contoso decides to expose the customer details the ReSTful way, it would have to think on the lines of:
Going by this theory, a URL that could probably provide details for a customer with ID = 9999 would look like:
http://www.contoso.com/Customer/Details/9999
Had there been no ReSTful way of retrieving customer details, one would have to send a SOAP request that would have probably looked like:
<?
xml
version
=
"1.0"
?>
<
soap:Envelope
xmlns:soap
"http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle
"http://www.w3.org/2001/12/soap-encoding"
>
soap:body
pb
"http://www.contoso.com/customer"
GetDetails
ID
>9999</
</
soap:Body
In the case of ReST, the URL is sent over an HTTP GET request to the server and the response is received in the form of raw data. In the traditional SOAP-way of invoking Web services, the XML request message is sent over an HTTP POST request to the server, and the response is embedded as the payload within the SOAP response envelope.
ReST URLs can also include parameters to handle more complicated requests. For example, to get customer details for John Smith, the URL might look like:
http://www.contoso.com/Customer/Details?firstName=John&lastName=Smith
To know more about ReST, you might want to go through the following links:
One of the real-life examples of ReST is the Twitter API.
In the previous section we saw how ReST is not SOAP and that ReST supports GET, PUT, POST, and DELETE methods. So, if one has to use ReST with BizTalk Server, the adapter to be used should be able to:
If we look at the adapters available with BizTalk Server 2010, only the HTTP adapter satisfies the first criteria but it does not support GET (it does support the POST method). Similarly, the webHttpBinding (that supports consuming and exposing ReSTful services) available with WCF-Custom adapter can send non-SOAP messages but that too has no way of specifying the HTTP verb to be used. All other adapters either do not support HTTP verbs or always exchange SOAP messages. So, there is no way to have an out-of-box BizTalk configuration to consume a ReSTful service. To use BizTalk Server with ReST, we'll have to do some customizations around HTTP adapter or the webHttpBinding of the WCF-Custom adapter. There is no prescribed way of doing customizations around the HTTP adapter. However, to the webHttpBinding, we can add some custom behaviors that when coupled with webHttpBinding enable BizTalk users to invoke and consume ReSTful services. Through these customizations, we can specify the following:
With WCF-Custom port configuration, there's no way of specifying these customizations directly on the webHttpBinding. So, we'll have to put these values in the request message. After specifying the values as part of the request message and adding a custom behavior to the webHttpBinding, when the request message hits the WCF-Custom port with the webHttpBinding, the custom behavior extracts the values from the request message and frames the required HTTP transport.
Now let us talk a little bit about the customization that would be required on the webHttpBinding. webHttpBinding is WCF-based. WCF enables you to alter messages before they are sent or after they are received through Message Inspectors. A message inspector is an extension to the client runtime and is configured as a behavior. A behavior, in turn, is added to a behavior extension element. This behavior extension element is registered in the machine.config with an element name, which makes it available to be configured as a custom behavior on a WCF-Custom port. For more information, see Extending WCF with Custom Behaviors.
So, to sum it up, to consume a ReSTful service with BizTalk Server, this is what we need to do:
In this article, we will use one of the Twitter ReST APIs that give us the public timeline and user-specific timeline. In Twitter parlance, a timeline is the list of tweets. For a public timeline, the list includes the last 20 tweets from unprotected users. For a user-specific timeline, the list includes the last 20 tweets by a specific unprotected user. An unprotected user is someone who has opted to not make the tweets private while setting up the Twitter account. There are two main reasons for picking these two APIs: both support the GET method and both do not require authentication. In this article, we want to keep the focus on how to use the customizations discussed above to use BizTalk Server with ReST. Including authentication with ReST would dilute the focus of this article. Moreover, authentication with ReST could vary with the APIs being used and will have to be handled differently for different applications. For example, Twitter uses OAuth for authenticating applications.
Coming back to the scenario, the user scenario for this article includes dropping a request message in a folder and out comes the list of tweets (for a public timeline or for a given user) in an e-mail. However, if you break this down into stages, this is what the scenario would look like:
Now that we understand the scenario, let's look at what all we need to create to have the solution work. We have two sets of requirement here, one for design-time where we will create a Visual Studio solution and one for the deployment-time where we will configure the BizTalk Server application by creating the physical ports in the BizTalk Server Administration console that will be required to send and receive messages.
The design-time, in turn, requires:
For the deployment-time, in the BizTalk Server Administration console, you need:
This article is written around a sample, Twitter_BTS, that is available at the MSDN Code Gallery. So, you could either use that sample and go through this article to understand how the sample was built or just use this article to create your own BizTalk Server application. This article is targeted towards the second approach so that you get to understand how this sample was built. Also, to be consistent with the sample, the names of artifacts (e.g. ports, schemas, classes, etc.) used in the article are same as that in the sample.
If you just want to use the sample, make sure you extract the sample files to C:\ root directory. Also, you can use the PortBindings.xml file to create the physical ports in the BizTalk Server Administration console. After you have created the physical ports using the bindings file, you'd need to update the SMTP port configuration to include the e-mail ID to which the tweets must be sent.
Let's start creating the solution.
At design-time, we will create a Visual Studio solution containing a BizTalk Server project and a C# class library and then finally we will deploy the solution. As part of setting up the design-time, we will perform the following tasks:
From the URLs and other information available on the links, we know that we need the following information to invoke the two Twitter APIs: The message format. For this scenario article, let us use XML. The supported method. For the two APIs we will be using in this scenario article, the supported method is GET. The URI for the two Twitter APIs. The content type for message exchange. Parameters, if any, required to frame the URL for the Twitter APIs.
From the URLs and other information available on the links, we know that we need the following information to invoke the two Twitter APIs:
As mentioned in the preceding section, all these required attributes will be passed in the request message. So, while creating the schema, we must ensure that we have attributes/elements that take these values. Let us now start creating the request message schema. Right-click the BizTalk Server project, point to Add, and then click New Item. In the Add New Item dialog box, select Schema Files, and in the right pane select Schema. Give the schema name as TwitterRequest.xsd, and then click Add. Create the attributes and elements as shown in the table below: Node name Type Property Name to be Set Property Value <Schema> - Element FormDefault Qualified Target Namespace http://microsoft.com/schemas/ samples/biztalkwebhttp/1.0 bizTalkWebHttpRequest Root node Data Structure Type bizTalkWebHttpRequest RootNode TypeName bizTalkWebHttpRequest method (The value for this attribute will specify the method to be invoked on the Twitter API) Child attribute to bizTalkWebHttpRequest Data Type xs:string uriTemplate (The value for this attribute will be used to frame the URL) Child attribute to bizTalkWebHttpRequest Data Type xs:string param Child record under bizTalkWebHttpRequest Data Structure Type keyValue Max Occurs unbounded Min Occurs 0 Mixed True name (The value for this attribute will be used as parameters appended to the URL) Child attribute to param Data Type xs:string header Child record under bizTalkWebHttpRequest Data Structure Type keyValue Max Occurs unbounded Min Occurs 0 Mixed True name (The value for this attribute will be used to specify the encoding in the message header that will be sent to invoke the Twitter API) Child attribute to header Data Type xs:string The following screen capture depicts the schema.
As mentioned in the preceding section, all these required attributes will be passed in the request message. So, while creating the schema, we must ensure that we have attributes/elements that take these values. Let us now start creating the request message schema.
Element FormDefault
bizTalkWebHttpRequest
method (The value for this attribute will specify the method to be invoked on the Twitter API)
uriTemplate (The value for this attribute will be used to frame the URL)
param
name (The value for this attribute will be used as parameters appended to the URL)
header
name (The value for this attribute will be used to specify the encoding in the message header that will be sent to invoke the Twitter API)
The following screen capture depicts the schema.
We will do a little bit of reverse engineering to get the response message schema. We will first get the XML by invoking the Twitter API from a Web browser and then use Visual Studio tools to generate the XSD from the XML.
Why do we need another schema for output message? Why can't we just send the response from Twitter, straight out? Well, you can of course do that and send the Twitter response as-is in an XML format out in an e-mail. However, if you look at the Twitter response by clicking the URL http://api.twitter.com/1/statuses/public_timeline.xml, you will see that the tweet is actually within the <text> element. All other elements just present some metadata either about the user or about the tweet. So, we want to have an output schema that has an element that maps to the <text> element in the tweet response so that all you see in the e-mail output is a list of tweets.
Even after we have mapped the <text> element from the Twitter response to an element in the output schema, the output would still be an XML document , which again does not make a good reading. So, we need an output schema that will send out the tweets in a format that is just like any other e-mail, say in an HTML format. Thankfully we can do so by creating a schema that represents the structure of an HTML document. So, we will create a schema that has the names and structure of an HTML tag. An HTML document typically has a paragraph element within a body element, which in turn, lies within the HTML element. We will create a schema with a similar structure as shown below:
html
body
p
></
Let's start creating the schema.
We should now use BizTalk Mapper to map the <text> element in the Twitter response schema to the <P> element in the output schema.
Let's now create an orchestration that will bind the entire application flow.
OutputResponse(Microsoft.XLANGs.BaseTypes.ContentType) =
"text/html"
;
The completed orchestration would resemble the following:
We are now done with creating all the required artifacts for a BizTalk Server project. Let us now start creating the C# class library.
The client message inspector class must implement the IClientMessageInspector interface. The two methods that must be implemented from IClientMessageInspector are AfterReceiveReply and BeforeSendRequest. In this article, we will not do anything as part of the AfterReceiverReply method because we don't want to inspect/alter the response from Twitter. However, as part of the BeforeSendRequest method, we will pull the RESTful configuration from the request message, configure the HTTP transport, and then send the ReST message body.
"C:\Program Files\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0
Tools\gacutil.exe" /iF “$(TargetPath)”
using
System;
System.Linq;
System.ServiceModel;
System.ServiceModel.Channels;
System.ServiceModel.Dispatcher;
System.Xml.Linq;
System.Xml.Schema;
namespace
MessageInspector
{
public
class
BizTalkWebHttpMessageInspector : IClientMessageInspector
private
static
readonly
XNamespace BizTalkWebHttpNs =
"http://microsoft.com/schemas/samples/biztalkwebhttp/1.0"
XName Request = BizTalkWebHttpNs +
"bizTalkWebHttpRequest"
XName Header = BizTalkWebHttpNs +
"header"
XName Param = BizTalkWebHttpNs +
"param"
XName Body = BizTalkWebHttpNs +
"body"
object
BeforeSendRequest(
ref
Message request, IClientChannel channel)
var requestBody = XElement.Load(request.GetReaderAtBodyContents());
if
(requestBody.Name != Request)
throw
new
XmlSchemaValidationException(
"Invalid request message. Expected "
+
Request +
", but got "
+ requestBody.Name +
"."
);
}
var bodyElement = requestBody.Element(Body);
var requestMessageProperty =
HttpRequestMessageProperty
Method = requestBody.Attribute(
"method"
).Value,
SuppressEntityBody = bodyElement ==
null
};
foreach
(var header
in
requestBody.Elements(Header))
requestMessageProperty.Headers.Add(
header.Attribute(
"name"
header.Value);
var uriTemplate =
UriTemplate(requestBody.Attribute(
"uriTemplate"
).Value);
request = bodyElement ==
? Message.CreateMessage(request.Version, request.Headers.Action)
: Message.CreateMessage(request.Version, request.Headers.Action, bodyElement);
request.Headers.To = uriTemplate.BindByName(channel.RemoteAddress.Uri,
requestBody.Elements(Param).ToDictionary(
e => e.Attribute(
).Value, e => e.Value));
request.Properties[HttpRequestMessageProperty.Name] = requestMessageProperty;
return
void
AfterReceiveReply(
Message reply,
correlationState)
// do nothing
An endpoint behavior class applies the message inspector to the client endpoint, so this class must implement the IEndpointBehavior interface. For more information, see Applying Custom Extensions with Behaviors.
System.ServiceModel.Description;
BizTalkWebHttpBehavior : IEndpointBehavior
Validate(ServiceEndpoint endpoint)
// Do nothing
AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
clientRuntime.MessageInspectors.Add(
BizTalkWebHttpMessageInspector());
In the previous section we created a behavior class that applies the message inspector to the client endpoint. We now need to make this behavior available to the configuration experience for the WCF-Custom adapter that will send the request message to Twitter. To make this behavior available for configuration we need to do two things: create a class that derives from the BehaviorExtensionElement and then register your BehavaiorExtensionElement in the <extensions>\<behaviorExtensions> element in the machine.config using an element name.
System.ServiceModel.Configuration;
BizTalkWebHttpElement : BehaviorExtensionElement
protected
override
CreateBehavior()
BizTalkWebHttpBehavior();
Type BehaviorType
get
typeof
(BizTalkWebHttpBehavior); }
add
name
"bizTalkWebHttp"
type
"MessageInspector.BizTalkWebHttpElement, MessageInspector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=71062dbfcab17aa3"
/>
We must now deploy the solution to the BizTalk Server Administration console. To deploy the application using Visual Studio, you must log on Windows as a member of the BizTalk Server Administrators group and run Visual Studio as administrator. Otherwise you will get the “Access is denied” error. The deployment process requires that assembly is strongly signed. You must sign your assemblies by associating the project with a strong name assembly key file. This file is provided with the sample.
Configuring a BizTalk Server application is mostly about binding. A binding creates a mapping between a logical endpoint, such as an orchestration port, and a physical endpoint, such as a send and receive port. This enables communication between different components of a BizTalk business solution. You can create bindings by using the BizTalk Server Administration console.
In the orchestration, we created three ports - one to receive the request message, one to send the request to Twitter and receive a response, and then one to send out the response from Twitter. If you look at the scenario described under Scenario for this Article:
We will now create the ports accordingly.
To test the solution, we will drop a request message to the DropRequestMessageHere folder under the location where you extracted the Twitter_BTS sample. The request message will be consumed by the receive location and you will get a list of tweets at the e-mail address you specified while configuring the SMTP send port.
Before testing the solution, let us have a recap of the main intent of this solution. The solution intends to get a list of tweets for a public timeline or a specific user by invoking the Twitter APIs through BizTalk Server. So, the request message must have all the information, plus any optional parameters, to frame the Twitter API. Let's first take a look at the Twitter API and the corresponding request message. For a public timeline:
encoding
"utf-8"
xmlns
method
"GET"
uriTemplate
"/statuses/public_timeline.xml?trim_user={TrimUser}&include_entities={IncludeEntities}"
"TrimUser"
>true</
"IncludeEntities"
"Content-Type"
>application/xml; charset=utf-8</
For a user-specific timeline (for user 'nitin_msft'):
"/statuses/user_timeline.xml?screen_name={ScreenName}"
"ScreenName"
>nitin_msft</
If you take a closer look at the API and the request messages, you would notice that:
If you now connect the dots, you would understand that the message inspector class that we created takes these values (uriTemplate, method, parameters, etc.) from the request message and frames the required URL for the API.
Both these request messages are available as part of the sample. Let's now test the solution using these two request messages.
If you received the e-mail responses as expected, you successfully invoked the Twitter ReST APIs using BizTalk Server 2010.
The sample, Twitter_BTS, built around the scenario discussed in this article can be downloaded from the MSDN Code Gallery.
You can use the solution in this article to retrieve the tweets for any unprotected user. You just need to include the user's screen name as a value within the <param name = "ScreenName"> element in the RequestTwitterXmlUserTimeline.xml. You can also add more parameters as you like to the user timeline request message. You would just need to:
Another approach one could take with respect to BizTalk Server + ReST is to expose BizTalk Services as ReSTful services. A couple of good writeups around this scenario are available at the following links:
Well, that's it. Go ReST :)