Scenario:
We have to consume a Rest Service (Or it can be WCF\Web service) in BizTalk with some headers, which will change per message. e.g. for each message the header values will be different. Lets Say we have to pass Username, Password and Signature in header. Signature is to be calculated as combination of UserName and GUID.
Solution:
Approach1: Using custom pipeline.
Simply we can create a Custom Pipeline and write the code but there is some challenge in that.
If we use custom pipeline, first message will go successfully, but from second message onwards, messages will not process.
It is because of the design of WCF-WebHttp adapter, it will not generate the headers per messages, and adapter uses the properties by port and not by message. So we cannot use it for these purpose.
Approach2: Using Dynamic Send port.
This approach works fine, but could not try. You can go through below link to get it.
Also it is tightly coupled and for any minor changes, it requires modification in Orchestration.
Approach3: Using wcf behavior extension.
This approach suits best for such scenario and it works 100% perfect.
Let’s first go to little description of the WCF Behavior, if you directly want to jump on code, Go to Code Section.
Implementating WCF Custom Behavior extension
Extension is something which you see in Behavior tab. So basically we have to create custom behavior and we bind it with extension.
Creating custom behavior
To create custom behavior we have to implement some interfaces. Below are the 2 interface required to implement.
- IEndpointBehavior
- IClientMessageInspector
Let’s go in little depth on how we can implement these interface.
- IEndpointBehavior
This interface implements the methods which we can use to extend the run-time behavior for and endpoint.
This interface contains four methods
- ApplyDispatchBehavior
- ApplyClientBehavior
- AddBindingParameters
- Validate
Let’s have little description of above 4 methods.
- ApplyDispatchBehavior
This method is used to create the object of context inspector class (which is implementing the interface IClientMEssageInspector) and bind that object to DispatchRuntime.MessageInspectors as shown in code below.
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
AddHttpHeaderContextInspector inspector = new AddHttpHeaderContextInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
- ApplyClientBehavior
This method is used to create object of the context inspector class (which is implementing the interface IClientMEssageInspector) and bind that object to clientRuntime.ClientMessageInspectors, as shown in code below:
public void ApplyClientBehavior(ServiceEndpoint endpoint, Client Runtime clientRuntime)
{
AddHttpHeaderContextInspector inspector = new AddHttpHeaderContextInspector(_interface Name);
clientRuntime.ClientMessageInspectors.Add(inspector);
return;
}
- AddBindingParameters
Implementation of this method can be as below
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
return;
}
- Validate
Implementation of this method can be as below
public void Validate (ServiceEndpoint endpoint)
{
return;
}
- IClientMessageInspector
This is the interface which implements the methods, which are used to inspect the message. Message can be inspected before sending the request to any service and after receiving response from the service Or message can be inspected after receiving request at server and before sending the response back to client.
By Inspection of message we mean that, we can tweak the message, add headers to message and many more things can be done.
This interface requires to implements the below four methods.
- AfterReceiveRequest
- BeforeSendReply
- AfterReceiveReply
- BeforeSendRequest
We can implement the methods as below
- AfterReceiveRequest
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
}
- BeforeSendReply
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
- AfterReceiveReply
public void AfterReceiveReply(ref Message reply, object correlationState)
{
string str = “”;
}
- BeforeSendRequest
public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel)
{
}
Apart from these we have to create one more class which inherits the BehaviorExtensionElement class to implement the CreateBehavior method.
So over all need to create 2 classes as below:
public class AddHttpHeaderContextBehaviorElement : BehaviorExtensionElement
public class AddHttpHeaderContextBehavior : IEndpointBehavior
public class AddHttpHeaderContextInspector : IClientMessageInspector
Code
Let’s come back to our topic “How to add dynamic header to consume any service from BizTalk”
Let’s go step by step:
- Create a class library project and above mentioned 3 classes
- Add below in namespaces for all 3 classes
System.ServiceModel.Description
System.ServiceModel.Channels
System.ServiceModel.Dispatcher
System.Runtime.Serialization
System.ServiceModel;
- Add references for below dll
System.ServiceModel
System.Runtime.Serialization
- Code for AddHttpHeaderContextBehaviorElement class
You can remove the unnecessary namespaces
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel.Configuration;
using System.Configuration;
namespace HttpHeaderExample.Component
{
public class AddHttpHeaderContextBehaviorElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get {return typeof(AddHttpHeaderContextBehavior); }
}
protected override object CreateBehavior()
{
return new AddHttpHeaderContextBehavior(INTERFACENAME);
}
// here we are creating the property of the extension which will appear in admin console. As we can use same extension to apply on different send ports. Now we can send different type of messages to different send port so to distinguish them in component we created this property. See picture below where we can see it in admin console. We can use biztalk message promoted properties as well inside the inspector.
[ConfigurationProperty(“interfaceName”)]
Public string INTERFACENAME
{
get { return (string) base[“interfaceName”]; }
set { base[“interfaceName”] = value; }
}
}
}
//End of class
- Code for AddHttpHeaderContextBehavior class
You can remove the unnecessary namespaces
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
namespace HttpHeaderExample.Component
{
public class AddHttpHeaderContextBehavior : IEndpointBehavior
{
public string _interfaceName;
public AddHttpHeaderContextBehavior(string interfaceName)
{
_interfaceName = interfaceName;
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{ //leave below commented
//AddHttpHeaderContextInspector inspector = new AddHttpHeaderContextInspector();
//endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
AddHttpHeaderContextInspector inspector = new AddHttpHeaderContextInspector(_interfaceName);
clientRuntime.ClientMessageInspectors.Add(inspector);
return;
}
public void Validate(ServiceEndpoint endpoint)
{
return;
}
}
}
- Code for AddHttpHeaderContextInspector class
You can remove the unnecessary namespaces
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Net;
using Newtonsoft.Json.Linq;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using Newtonsoft.Json;
namespace HttpHeaderExample.Component
{
public class AddHttpHeaderContextInspector : IClientMessageInspector
{
public string _httpMethod;
public string _uri;
public string _basicAuthUserName;
public string _basicAuthPassword;
public string _interfaceName;
public string content;
public string signatureKey;
public string operationName;
public AddHttpHeaderContextInspector(string interfaceName)
{
_interfaceName = interfaceName;
}
// Below two methods not reuired to implement for this example
// you can use these if you host some service on server and want manipulation in //the message after receive request and before send reply back to client.
//public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
//{
//}
//public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
//{
//
//}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// not mainupulating the message after receiving reply from zycus
string str = “”;
}
public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel)
{
Message newMessage = null;
try
{
// Get the http request and method
HttpRequestMessageProperty httpRequest = null;
if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name))
{
httpRequest = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
}
// Fetch the URI and method , you can fetch biztalk message property here which is promoted.
_httpMethod = httpRequest.Method.ToLower();
_uri = request.Headers.To.AbsoluteUri;
// fetch the operation name
#region Extract the xml message
// Extract the message only for methods other than get
if (!(_httpMethod.Equals(“get”)))
{
// the data coming here is in Raw format so used below
// methods, even the message content is json but in this method it //was received as raw message. At the time of debugging you can see which type of data is //coming and accordingly you can use ReadContent method.
MessageBuffer msgbuf = request.CreateBufferedCopy(int.MaxValue);
Message tmpMessage = msgbuf.CreateMessage();
XmlDictionaryReader xdr = tmpMessage.GetReaderAtBodyContents();
xdr.ReadStartElement(“Binary”);
byte[] bodyBytes = xdr.ReadContentAsBase64();
string messageBody = Encoding.UTF8.GetString(bodyBytes);
// Here we are calling the Rest service which accepts the json //message so receving json string here.if you call normal wcf service then it will be xml //message.
// copy json message body to content
content = messageBody;
// we can tweak here the message content
Content = content +”Add some string if we want to add”
// Using newton soft json library we can read the json object and //mainupulate that. Also we can use here interfaceName property to distinguished the //different messages and tweak them. Lets say there are two different services and we //have to use same component for them. But the message that should go have different //string to be added , so at admin console configuration provide different name for //property interfaceName. That property value will be available here and we can tweak //accordinglty.
//e.g if (interfaceName == “A”)
//content = content+A;
//else
//content = content+B;
// Now to recreate the message
// after mainupulating the message it needs to be send back to //stream
bodyBytes = Encoding.UTF8.GetBytes(content);
MemoryStream ms = new MemoryStream();
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(ms);
writer.WriteStartElement(“Binary”);
writer.WriteBase64(bodyBytes, 0, bodyBytes.Length);
writer.WriteEndElement();
writer.Flush();
ms.Position = 0;
XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(ms, XmlDictionaryReaderQuotas.Max);
newMessage = Message.CreateMessage(reader, int.MaxValue, request.Version);
newMessage.Properties.CopyProperties(request.Properties);
newMessage.Headers.CopyHeadersFrom(request);
request = newMessage;
msgbuf.Close();
}
//Message manipulation is completed here
#endregion
// Now suppose we have to pass the user name password and signature in header.
// and signature has to create here using username+new guid;
//So User name and password directly can be taken from sso config or bre or as per //requirement
//Signature can be calculated here
#region Fetch UserName, Password,
_basicAuthUserName = “Some User Name”
_basicAuthPassword = “Some password”
signatureKey = _basicAuthUserName+”New guid”(it will change permessage);
#endregion
#region Add Headers to Message
WebHeaderCollection headers = httpRequest.Headers;
headers.Add(“username”, _basicAuthUserName);
headers.Add(“signature”, signatureKey);
headers.Add(HttpRequestHeader.ContentType, “application/json”);
#endregion
#endregion
}
catch (Exception ex)
{
throw ex;
}
return null;
}
}
}
- Coding part complete here. Now build the project and add it go GAC.
- In order this newly created behavior to be appear in BizTalk admin console, we need to add the component in machine.config file
- You can find machine.config file at below location and this file need to be updated for both 32 bit and 64 bit folder.
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config – this is the location for 32bit machine.config
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config – this is the location for 64 bit machine.config
V4.0.30319 is the latest framework of the dot net installed in machine.
First take back up of machine.config files, it is very critical file and can down entire system.
Make sure that below entry is present in machine.config with siebleAdapter.
This way we can achieve sending dynamic headers to consume rest\wcf service in biztalk.