使用 REST API 将消息发送到 .Net Core Core 中的 azure 服务总线队列

Sending a message to a azure service bus queue in .Net Core Core using the REST API

我想向 .Net Core 中的 Azure 服务总线队列发送消息,但 WindowsAzure.ServiceBus 包与 .Net Core 不兼容。

谁能告诉我如何使用 REST API 向队列发送消息?

Can anyone show me how to send a message to the queue using the REST API?

正如 4c74356b41 在他的评论中提到的,我们可以通过 this REST API:

向 Azure 服务总线队列发送消息

POST http{s}://{serviceNamespace}.servicebus.windows.net/{queuePath|topicPath}/messages

这里有一个例子

在上面的请求中,我提供了一个Shared Access Signature(token),生成一个Shared Access Signature(token),请参考this article

current client is not .NET Core compatible, the new client, that is a work in progress, is 100% compatible. The pre-release package will be available on April 3rd and the status can be tracked here。您可以下载课程代码并在今天编译它,但需要注意的是 API 会随着团队试图充实设计细节而发生变化。

感谢 Fred 的回答,我已经扩展到包括如何 post 身份验证 header 和签名。

public class AzureServiceBusSettings
{
    public string BaseUrl { get; set; }
    public string SharedAccessKey { get; set; }
    public string SharedAccessKeyName { get; set; }
}

public interface IServiceBus
{
    /// <summary>
    /// Publish domain events to domain topic.
    /// </summary>
    Task PublishAsync<T>(T @event)

    /// <summary>
    /// Send commands to command queue.
    /// </summary>
    Task SendAsync<T>(T command)
}

public class ServiceBus : IServiceBus
{
    private readonly AzureServiceBusSettings _settings;

    public ServiceBus(IOptions<AzureServiceBusSettings> azureServiceBusSettings)
    {
        _settings = azureServiceBusSettings.Value;
    }

    /// <summary>
    /// Publish domain events to domain topic.
    /// </summary>
    public async Task PublishAsync<T>(T @event)
    {
        await SendInternalAsync(@event, "domain");
    }

    /// <summary>
    /// Send commands to command queue.
    /// </summary>
    public async Task SendAsync<T>(T command)
    {
        await SendInternalAsync(command, "commands");
    }

    private async Task SendInternalAsync<T>(T command, string queueName)
    {
        var json = JsonConvert.SerializeObject(command);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        using (var httpClient = new HttpClient())
        {
            httpClient.BaseAddress = new Uri(_settings.BaseUrl);

            try
            {
                var url = $"/{queueName}/messages";

                httpClient.DefaultRequestHeaders.Authorization =
                    new AuthenticationHeaderValue("SharedAccessSignature", GetSasToken(queueName));

                var response = await httpClient.PostAsync(url, content);

                // Success returns 201 Created.
                if (!response.IsSuccessStatusCode)
                {
                    // Handle this.
                }
            }
            catch (Exception ex)
            {
                // Handle this.
                // throw;
            }
        }
    }

    private string GetSasToken(string queueName)
    {
        var url = $"{_settings.BaseUrl}/{queueName}";
        // Expiry minutes should be a setting.
        var expiry = (int)DateTime.UtcNow.AddMinutes(20).Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
        var signature = GetSignature(url, _settings.SharedAccessKey);
        var token = $"sr={WebUtility.UrlEncode(url)}&sig={WebUtility.UrlEncode(signature)}&se={expiry}&skn={_settings.SharedAccessKeyName}";
        return token;
    }

    private static string GetSignature(string url, string key)
    {
        var expiry = (int)DateTime.UtcNow.AddMinutes(20).Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
        var value = WebUtility.UrlEncode(url) + "\n" + expiry;
        var encoding = new UTF8Encoding();
        var keyByte = encoding.GetBytes(key);
        var valueBytes = encoding.GetBytes(value);
        using (var hmacsha256 = new HMACSHA256(keyByte))
        {
            var hashmessage = hmacsha256.ComputeHash(valueBytes);
            var result = Convert.ToBase64String(hashmessage);
            return result;
        }
    }
}

以及 post 的简单 xunit 测试:

public class ServiceBusTests
{
    public class FooCommand : ICommand
    {
        public Guid CommandId { get; set; }
    }

    private Mock<IOptions<AzureServiceBusSettings>> _mockAzureServiceBusOptions;

    private ServiceBus _sut;

    public ServiceBusTests()
    {
        var settings = new AzureServiceBusSettings
        {
            BaseUrl = "https://my-domain.servicebus.windows.net",
            SharedAccessKey = "my-key-goes-here",
            SharedAccessKeyName = "RootManageSharedAccessKey"
        };

        _mockAzureServiceBusOptions = new Mock<IOptions<AzureServiceBusSettings>>();
        _mockAzureServiceBusOptions.SetupGet(o => o.Value).Returns(settings);

        _sut = new ServiceBus(
            _mockAzureServiceBusOptions.Object);
    }

    [Fact]
    public async Task should_send_message()
    {
        // Arrange.
        var command = new FooCommand {CommandId = Guid.NewGuid()};

        // Act.
        await _sut.SendAsync(command);

        // Assert.
        // TODO: Get the command from the queue and assert something.
    }
}