Pages

BizTalk : How To : Call a Web service Using Custom Pipeline in a Messaging Solution

Wednesday, April 10, 2013


In this article I'll explain how you can call a Web Service which requires multiple arguments using a Custom pipeline and a custom pipeline component in a messaging-only scenario without using any Orchestration.

Normally, when there is a requirement to call a web service from BizTalk, people tend to take the easy route of calling it via an Orchestration. When we do a web reference inside the orchestration, Orchestration does quite a lot of work for us. It creates all the required schemas, it creates all the required multipart messages, which will be passed to the web service as argument. It makes our life easier. But I guess like me, some of you out there might need to call the web service without using Orchestration. As shown in the above figure. I've one request-response HTTP receive port, and one Solicit response SOAP send port, through this I'm going to call a web service, which expects multiple argument (including one complex type) and return the result back to the caller (HTTP Response). Here are the steps: The attached sample file contains all the required file, I'm just going to explain the key factors in this article.
1. Web Service Definition:
[WebMethod]
public Person GetPersonInfo(Person person, string firstName, string secondName) {
//Some processing
return person;
}
2. Create a general custom pipeline component to construct the multipart message required for the Web Service call 
At run time SOAP Adapter in the send port will map the Biztalk multipart IBaseMessage to the Web Service argument based on the partName of IBaseMessage and argument names of the Webservice. The key factor is how we are going to construct the multipart message in the format required by the SOAP Adapter to make the WebService call. So, in this article we are going to create custom pipeline component which will construct the correct IBaseMessage required by the SOAP adapter based on the input message and some pipeline design time properties
The custom pipeline component we are going to use has 2 design time properties FirstName and SecondName, which will be passed as parameters to the web service (See webservice definition from Step 1). We'll pass the first webservice argument "Person" as the incoming message via HTTP receive port. The figure below show the custom design time properties configuration window within Biztalk Admin console. 
The code below is the snippet from the custom pipeline component (two important methods Execute and CreateMessage). The Execute method below without the first line of code will be equivalent to aPassThru pipeline component with default Biztalk IBaseMessage. 
#############################################################
public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(IPipelineContext pc, IBaseMessage inmsg)
{
IBaseMessage msg = CreateMessage(inmsg.BodyPart.GetOriginalDataStream(), pc.GetMessageFactory(),inmsg.Context);
return msg;
}
#############################################################
IBaseMessage CreateMessage(Stream s, IBaseMessageFactory msgFactory, IBaseMessageContext context)
{
IBaseMessage msg = msgFactory.CreateMessage();
IBaseMessagePart part = msgFactory.CreateMessagePart();
part.Data = s;
msg.AddPart("Person", part, true);
msg.Context = context;
//1st Part
IBaseMessagePart partFirstName = msgFactory.CreateMessagePart();
byte[] firstPart = System.Text.Encoding.UTF8.GetBytes(string.Format("<string>{0}</string>", _firstName));
partFirstName.Data = new MemoryStream(firstPart);
partFirstName.Charset = "utf-8"
partFirstName.ContentType = "text/xml"
msg.AddPart("firstName", partFirstName, false);
//2nd Part
IBaseMessagePart partSecondName = msgFactory.CreateMessagePart();
byte[] secondPart = System.Text.Encoding.UTF8.GetBytes(string.Format("<string>{0}</string>", _secondName));
partSecondName.Data = new MemoryStream(secondPart);
partSecondName.Charset = "utf-8"
partSecondName.ContentType = "text/xml"
msg.AddPart("secondName", partSecondName, false);
return msg;
}
#############################################################
Our user defined function CreateMessage will create the required BizTalk IBaseMessage as shown in the below figure
In the above code snippet, the important things to note are highlighted in RED. The incoming message ("Person") will go as the first part (BodyPart) of the IBaseMessage with the name "Person", and then we added two more addional parts "firstName" and "secondName" to the IBaseMessage with correct partNames inline with the web service arguments. The other important thing to note is how the basic data types gets serialized. In our example we got "<string>{0}</string>" as value for firstName and secondName, because they are of type string. If for example you got int as your argument then you need to create the part in the format <int>5</int>.
NOTE: See the web service signature defined in Step 1 for comparison
3. Create a Custom Receive Pipeline using the custom pipeline component
Create a new Biztalk Receive Pipeline and place the custom pipeline component we created in the "Decode" stage of the pipeline.
4. Configure the ports
As shown in our design diagram at the beginning we need 2 ports to send and receive the message, the attached sample file got a binding file, this section is just for explanation, doesn't explain in detail how to configure the ports. Make sure the URL are correct, both on Receive and Send side after importing the binding. You need to configure IIS as well to receive messages via HTTP, follow the link to configure IIS for HTTP receive  http://msdn2.microsoft.com/en-us/library/aa559072.aspx.
Two-Way HTTP Receive Port:
Solicit-Response SOAP Send Port:
We used the .NET Proxy class on our SOAP port to make the call.
Filter Condition on the Send Port
5. Post a Message.
I used WFetch to post the message to BizTalk. You can see on the result pane the request message is posted and you got the response back from the web service synchronously on a two way connection.
Troubleshooting:
Some of the common exceptions you'll see while calling a webservice via SOAP adapter is shown below (from HAT and eventviewer)
1. "Failed to retrieve the message part for parameter "firstName". "
2. "Failed to serialize the message part "firstName" into the type "String" using namespace "". Please ensure that the message part stream is created properly."
The reason for the first error message is due to wrongly named IBaseMessage partName. ReadSection 2 carefully to overcome this error.
The reason for the second error message is mainly due to some problem with serializing the IBaseMessage parts to the correct web service arguments. Best approach to overcome this error will be to build a .net console/windows application, add a web reference to the webservice and try to serialize each argument to the corresponding type. For example for this example you can try the following
FileStream fs = new FileStream(@"C:\Documents and Settings\SaravanaK\Desktop\FailedMessages\_Person.out",FileMode.Open,FileAccess.Read);
XmlSerializer serialise = new XmlSerializer(typeof(LH.WebReference.Person));
LH.WebReference.Person per = (LH.WebReference.Person)serialise.Deserialize(fs);
fs.Close();
fs = new FileStream(@"C:\Documents and Settings\SaravanaK\Desktop\FailedMessages\_secondName.out",FileMode.Open,FileAccess.Read);
serialise = new XmlSerializer(typeof(string));
string s2 = (string)serialise.Deserialize(fs);
fs.Close();
The files "_Person.out" and "_secondName.out" are saved from HAT tool. See the exception detail and fix the issue, it will be some namespace issue or data issue.
Read the readme.txt file inside to configure it. Will take approximately 5-20 minutes based on your BizTalk knowledge level.

No comments:

Post a Comment

Post Your Comment...