Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Azure ServiceBus from Xamarin Forms PCL

How is brokered messaging via Azure Service Bus done from a Xamarin Forms PCL...is there an SDK, library or plugin? If there is a way to hand roll a brokered message, I suppose it could be accomplished with an HttpClient & the REST API ...

like image 803
Brooks Clark Avatar asked Sep 28 '16 16:09

Brooks Clark


1 Answers

I finally have a working method for posting a message to an Azure Service Bus Queue from a Xamarin PCL! The way to do this is via an HttpClient.

Gotchas that made the solution elusive:

  • The nuget package for Microsoft.ServiceBus.Messaging will happily install, but has no exposure to the portable class.

  • WebClient examples abound, but there is no webclient available in the PCL!

  • PCL has no native crypto for HMACSHA256 for Shared Access Signature tokens. Get the PCLCrypto package from nuget.
  • Some documentation says request headers of Content-Type : application/atom+xml;type=entry;charset=utf-8, and BrokerProerties are required. Ain't so!
  • The path to post to is: BaseServiceBusAddress + queue + "/messages"

    public const string ServiceBusNamespace = [YOUR SERVICEBUS NAMESPACE];
    
    public const string BaseServiceBusAddress = "https://" + ServiceBusNamespace + ".servicebus.windows.net/";
    
    /// <summary>
    /// The get shared access signature token.
    /// </summary>
    /// <param name="sasKeyName">
    /// The shared access signature key name.
    /// </param>
    /// <param name="sasKeyValue">
    /// The shared access signature key value.
    /// </param>
    /// <returns>
    /// The <see cref="string"/>.
    /// </returns>
    public static string GetSasToken(string sasKeyName, string sasKeyValue)
    {
        var expiry = GetExpiry();
        var stringToSign = WebUtility.UrlEncode(BaseServiceBusAddress ) + "\n" + expiry;
    
        var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
        var hasher = algorithm.CreateHash(Encoding.UTF8.GetBytes(sasKeyValue));
        hasher.Append(Encoding.UTF8.GetBytes(stringToSign));
        var mac = hasher.GetValueAndReset();
        var signature = Convert.ToBase64String(mac);
    
        var sasToken = string.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}", WebUtility.UrlEncode(baseAddress), WebUtility.UrlEncode(signature), expiry, sasKeyName);
        return sasToken;
    }
    
    /// <summary>
    /// Posts an order data transfer object to queue.
    /// </summary>
    /// <param name="orderDto">
    /// The order data transfer object.
    /// </param>
    /// <param name="serviceBusNamespace">
    /// The service bus namespace.
    /// </param>
    /// <param name="sasKeyName">
    /// The shared access signature key name.
    /// </param>
    /// <param name="sasKey">
    /// The shared access signature key.
    /// </param>
    /// <param name="queue">
    /// The queue.
    /// </param>
    /// <returns>
    /// The <see cref="Task"/>.
    /// </returns>
    public static async Task<HttpResponseMessage> PostOrderDtoToQueue(OrderDto orderDto, string serviceBusNamespace, string sasKeyName, string sasKey, string queue)
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri(BaseServiceBusAddress);
            client.DefaultRequestHeaders.Accept.Clear();
    
            var token = GetSasToken(sasKeyName, sasKey);
            client.DefaultRequestHeaders.Add("Authorization", token);
    
            HttpContent content = new StringContent(JsonConvert.SerializeObject(orderDto), Encoding.UTF8);
            content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    
            var path = BaseServiceBusAddress + queue + "/messages";
    
            return await client.PostAsync(path, content);
        }
    }
    
    /// <summary>
    ///     Gets the expiry for a shared access signature token
    /// </summary>
    /// <returns>
    ///     The <see cref="string" /> expiry.
    /// </returns>
    private static string GetExpiry()
    {
        var sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
        return Convert.ToString((int)sinceEpoch.TotalSeconds + 3600);
    }
    

    }

like image 189
Brooks Clark Avatar answered Oct 24 '22 05:10

Brooks Clark