Implementation of single receive location to process GET/POST(Json) messages

Sorry all for not writing any post for last couple of months. I was involve in a very good and interesting project with one of our major client. We used Rest services to the full of its ability to cater the business scenario. I though to pen this task down, so that I remember the good work :).

This article will describe how we can implement solution to receive GET/POST messages into the BizTalk using WCF-Webhttp adapter. This article will also describe how to use IProbeMessage interface in the pipeline component with the simple usage.

I am using Newtonsoft.Json.dll to convert Json into xml. However, with the release of BizTalk 2013 R2, BizTalk will have its own out-of box Json adapter.

1- GET- Create Schema for converting the URL property(query) to schema message and promote these fields.

image

2- POST – Create Schema for converting Json message to the XML using this schema.

image

3- Create Pipeline Component.

  • GetRestContext :- This pipeline component is used to get the query information from the context property and transform it into the schema (document spec provided as a property). This uses the IprobeMessage interface to check if the the dissembler stage in the component need to be executed. To use IProbeMessage you need to extend your class to use interface IProbeMessage.

           public bool Probe(IPipelineContext pContext, IBaseMessage pInMsg)
       {
           // Check arguments
           if (null == pContext)
               throw new ArgumentNullException(“pContext”);

           // Check whether input message doesn’t have a body part or it is set to null, fail probe in those cases
           if (pInMsg.BodyPart.Data.Length == 0)
               return true;

           return false;
       }

   I am considering it is GET call if the in coming message is not having data body and return true. Return True will execute the below dissembler stage component and False it ignore and will go to the next pipeline component in the custom pipeline. There is much better way of checking the GET/POST using the context property.

#region IDisassemblerComponent
private System.Collections.Queue _qOutMessages = new System.Collections.Queue();
public void Disassemble(IPipelineContext pContext, IBaseMessage pInMsg)
{

     //Get a reference to the BizTalk Schema
     var documentSpec = (DocumentSpec)pContext.GetDocumentSpecByName(DocumentSchema);
     var annotations = documentSpec.GetPropertyAnnotationEnumerator();
     var doc = new XmlDocument();
     var sw = new StringWriter(new StringBuilder());

     //create new instance of the schema.
     doc.Load(documentSpec.CreateXmlInstance(sw));
     sw.Dispose();

     while (annotations.MoveNext())
     {
         var annotation = (IPropertyAnnotation)annotations.Current;
         var node = doc.SelectSingleNode(annotation.XPath);
         var propertyValue = pInMsg.Context.Read(annotation.Name, annotation.Namespace);
         if (propertyValue != null)
         {
             node.InnerText = propertyValue.ToString();
         }
     }
     var ms = new MemoryStream();
     doc.Save(ms);
     ms.Seek(0, SeekOrigin.Begin);
     var outMsg = pInMsg;
     outMsg.BodyPart.Data = ms;
     outMsg.Context.Promote(“MessageType”, “http://schemas.microsoft.com/BizTalk/2003/system-properties”, documentSpec.DocType);
     outMsg.Context.Promote(“SchemaStrongName”, “http://schemas.microsoft.com/BizTalk/2003/system-properties”, documentSpec.DocSpecStrongName);
     _qOutMessages.Enqueue(outMsg);

}
public IBaseMessage GetNext(IPipelineContext pContext)
{
     if (_qOutMessages.Count > 0)
         return (IBaseMessage)_qOutMessages.Dequeue();
     else
         return null;
}
#endregion

  • GetXMLfromJSON : This component is using the Newtonsoft.Json.dll to convert Json message into XML. Same as above the document specification is provided as the property and uses IProbMessage interface to check whether it is GET or POST.

        public bool Probe(IPipelineContext pContext, IBaseMessage pInMsg)
       {
           // Check arguments
           if (null == pContext)
               throw new ArgumentNullException(“pContext”);

           // Check whether input message doesn’t have a body part or it is set to null, fail probe in those cases
           if (pInMsg.BodyPart.Data.Length ==0)
               return false;

           return true;
       }

    Iprobemessage decide whether to execute the dissembler stage component or not. If return true it will execute the below Json to xml converts.

private System.Collections.Queue _qOutMessages = new System.Collections.Queue();
      public void Disassemble(IPipelineContext pc, IBaseMessage inmsg)
      {

           object operation = null;
          operation = inmsg.Context.Read(OPERATION_NAME_PROPNAME, SYSTEM_PROPERTIES_NS);
          string operationName = (operation ?? (object)string.Empty) as string;
              // Make message seekable
              if (!inmsg.BodyPart.Data.CanSeek)
              {
                  var originalStream = inmsg.BodyPart.Data;
                  Stream seekableStream = new ReadOnlySeekableStream(originalStream);
                  inmsg.BodyPart.Data = seekableStream;
                  pc.ResourceTracker.AddResource(originalStream);
              }
            //Loading full message in the memory is bad practice, but this is how Newtonsoft.Json.dll work. The library wants to create a string out of it. Hope BizTalk 2013 R2 out-of box Json converter will give relief.
              MemoryStream jsonStream = new MemoryStream();
              inmsg.BodyPart.Data.CopyTo(jsonStream);
              inmsg.BodyPart.Data.Seek(0, SeekOrigin.Begin);

              var jsonString = jsonStream.Length == 0
                                      ? string.Empty
                                      : Encoding.GetEncoding(inmsg.BodyPart.Charset ?? Encoding.UTF8.WebName).GetString(jsonStream.ToArray());
              jsonString = “{\”” + this.ParrentNode + “\”:” + jsonString + “}”;

              var rawDoc = JsonConvert.DeserializeXmlNode(jsonString, string.IsNullOrWhiteSpace(this.RootNode) ? operationName : this.RootNode);

               // Here we are ensuring that the custom namespace shows up on the root node
              // so that we have a nice clean message type on the request messages

              var xmlDoc = new XmlDocument();
              xmlDoc.AppendChild(xmlDoc.CreateElement(DEFAULT_PREFIX, rawDoc.DocumentElement.LocalName, this.Namespace));
              xmlDoc.DocumentElement.InnerXml = rawDoc.DocumentElement.InnerXml;

              writeMessage(inmsg, xmlDoc);
          var ms = new MemoryStream();
          xmlDoc.Save(ms);
          ms.Seek(0, SeekOrigin.Begin);
          var outMsg = inmsg;
          outMsg.BodyPart.Data = ms;
          outMsg.Context.Promote(“MessageType”, “http://schemas.microsoft.com/BizTalk/2003/system-properties”, this.Namespace + “#” + this.RootNode);
          _qOutMessages.Enqueue(outMsg);

      }

       private static void writeMessage(Microsoft.BizTalk.Message.Interop.IBaseMessage inmsg, XmlDocument xmlDoc)
       {
           var outputStream = new VirtualStream();

           using (var writer = XmlWriter.Create(outputStream, new XmlWriterSettings()
           {
               CloseOutput = false,
               Encoding = Encoding.UTF8
           }))
           {
               xmlDoc.WriteTo(writer);
               writer.Flush();
           }

           outputStream.Seek(0, SeekOrigin.Begin);

           inmsg.BodyPart.Charset = Encoding.UTF8.WebName;
           inmsg.BodyPart.Data = outputStream;
       }
       public IBaseMessage GetNext(IPipelineContext pContext)
       {
           if (_qOutMessages.Count > 0)
               return (IBaseMessage)_qOutMessages.Dequeue();
           else
               return null;
       }

4- Create Custom Pipeline. Include these two pipeline component to the Pipeline component folder in the BizTalk installation folder. Create custom pipeline component and add as per below.

image 

5- Deploy the above.

6- Create Rest Web service using the WCF wizard.

7- Receive Location should look like the below.

image

Open the Rcv_GetRestContext custom pipeline.

Enter the Get Document Schema information in the Stage1: Disassemble-Component(1).

Enter Namespace., ParentNode, RootNode for the POST schema in the Stage1: Disassemble Component(2).

image

Click on Wcf-WebHttp Configuration.

Add HTTP Method and URL Mapping for GET and POST method. I need to add a resource to identify each separate call.

<BtsHttpUrlMapping>
<Operation Name = ‘Receive_1′ Method=’POST’ Url=’/POSTTest’ />

<Operation Name = ‘Receive_2′ Method=’GET’ Url=’/GETTest?field1={field1}&amp;field2={field2}}&amp;field3={field3}’ />

</BtsHttpUrlMapping>

Add the variable mapping for the promoted field.

8- Test the solution by using SOAPUI.

  • GET –

              EndPoint :- https://localhost/BTSREST/REST.svc

             Resource :- /GETTest

            Parameters:- field1=abc & field2=xyz & field3 = efg

https://localhost/BTSREST/REST.svc/GETTest?field1=abc&field2=xyz&field3=efg

The message received on the receive port will convert the above parameter in the schema provided in the DocumentSchema property of the Component(1) and drop this message into message box.

  • POST –

         EndPoint:- https://localhost/BTSREST/REST.svc

        Resource- /POSTTest

        Media Tyupe = application/json

        copy the below json to the Post method.

{
  “ns0:POST”: {
    “-xmlns:ns0”: “http://BizTalk_Server_Project2.Post”,
    “Json”: {
      “Field1”: “Field1_0”,
      “Field2”: “Field2_0”,
      “Field3”: “Field3_0”,
      “Field4”: “Field4_0”,
      “Field5”: “Field5_0”
    }
  }
}

The receive location will create xml from json using the pipeline component and drop the message into message box.

Please let me know if someone have a better approach then this. Hope this help people in REST world :).

Thank You.

Advertisements

4 thoughts on “Implementation of single receive location to process GET/POST(Json) messages

  1. Hi Sohil,
    Sorry unfortunately I won’t be able to share the code, actually it is customer specific. I think I have mentioned enough on this post for you to go on, let me know if you need any help.

    Regards,
    Shadab

    • Its ok Shadab, I just need your help in implementing the disassemble component in the pipeline.

      I don’t get the clue from where shall I specify the document schema in the documentspecsname method.
      Should we place the void Disassemble in the IBaseMessage Execute?

      Thanks:)

      • Hi Sohil,
        The document schema for converting json into xml (POST method) is provided in the pipeline component property. You need to configure the document specs name in the piple component and this will pass the value during run time.

        Same way the get method documentspec is provided through the pipeline property. You need to configure this as well. Hope this help.

        Regards,
        Shadab

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s