Archive

Archive for August, 2011

RESTifing BizTalk – Part I

August 9, 2011 Leave a comment

Recently, I and Leandro Diaz Guerra had a chance to teach BizTalk speak REST. With introduction of the webHttpBinding, this, previously challenging task, has become much easier. However, the communication over HTTP stack doesn’t make it RESTful. REST is an architectural style that requires careful design of resources, resource navigation and their relations. Translating a trivial BizTalk’s de-batching task into RESTful terms will make the “Batch” a parent resource of the individual messages with following addressable schema:

The information in curly braces represents dynamic parameters which creates a challenge for BizTalk WCF port configuration. The solution to this problem was to modify WebManualAddressingBehavior.cs  WCF custom extensions very well described in Leandro’s blog here. But first, a little bit about of the BizTalk’s process, discipline and Uri template to model REST resources.

By the time the batch picked up by BizTalk process the batch resource already exist with unique {InterchangeId}. The batch gets de-batch within a receive pipeline and produces bunch of messages with unique {MessageId}and the same {InterchangeId}. The Send port , pickups these messages and calls external RESTFul service. It is also solely responsible for building HTTP header, define payload, injecting values into the dynamic resource Uri template and make a POST call. There are three places within Send Port configuration where you could specify URI template:

  • Address (URI) on general tab
  • SOAP action header

image

  • Defining ManualAddress property of the WebManualAddressing custom WCF extension

image

The rules of thumb I use are following:

  • use Address URI only to specify service endpoint. In our case it always will be http://host/batches
  • Use SOAP action header to specify location of resource, reserving WebManualAddress property to override SOAP action header in some rare cases.

Here is a summary of logical changes I need to make within WebManualAddress behavior:

  • Check if WebManualAddress property is defined and if not get it from SOAP action
    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
     try
       {
        if (String.IsNullOrWhiteSpace(ManualAddress.OriginalString))
         {
          this.ManualAddress = new Uri(request.Headers.Action);
         }
       request.Headers.To = new Uri(ApplyManualAddressMacro(ManualAddress.OriginalString
                                    , request.Properties));
        }
      catch (Exception ex)
          {
            Debug.Write(ex.Message, AppDomain.CurrentDomain.FriendlyName);
          }
      return request;
     }
  • Substitute dynamic parameter with their values 
    private string ApplyManualAddressMacro (string manualAddress, 
                                            MessageProperties messageProperties)
            {
                var uriAddress = HttpUtility.UrlDecode(manualAddress);
                const string regexExpression = @"%(\w*)%";
                try
                {
                    var regex = new Regex(regexExpression);
                    var matchCollection = regex.Matches(uriAddress);
                    for (var i = 0; i < matchCollection.Count; i++)
                    {
                        var captures = matchCollection[i].Captures;
                        foreach (var capture in captures)
                        {
                            string value = capture.ToString();
                            var replacement = GetCachedValue(value.Replace("%", "")
                                , messageProperties).Replace("{","").Replace("}","");
                            uriAddress = uriAddress.Replace(value, replacement);
                        }
                    }
                    return uriAddress;
                }
                catch (Exception ex)
                {
                    Debug.Write(ex.Message , AppDomain.CurrentDomain.FriendlyName);
                    return manualAddress;
                }
            }

The GetCachedValue is just an optimization method which caches relation between template keys %InterchangeId% retrieved from http://host/batches/%InterchangeID%  in this case   and  BizTalk fully qualified named context properties  ( namespace + name  http://schemas.microsoft.com/BizTalk/2003/system-properties/#InterchangeId). I use standard .NET Cache and store them for 10 minutes

public string GetCachedValue(string key, IEnumerable<KeyValuePair<string, object>> messageProperties)
       {
           var cache = MemoryCache.Default;
           var cacheKey = string.Format("WebManualAddressingBehavior_{0}", key);
           if (!cache.Contains(cacheKey))
           {
               var item = messageProperties.Where(p => p.Key.Contains(key)).FirstOrDefault();
               if (item.Value != null)
               {
                   cache.Set(cacheKey, item.Value, 
                       new DateTimeOffset(DateTime.Now.AddSeconds(60 * 10)));
               }else
               {
                   return key;
               }
           }
           return cache[cacheKey].ToString();
       }
Categories: BizTalk, REST Tags: