尝试使用 Azure 通知中心批量发送时出现 403 禁止
Posted
技术标签:
【中文标题】尝试使用 Azure 通知中心批量发送时出现 403 禁止【英文标题】:403 Forbidden when attempting to use Azure Notification Hub Batch Send 【发布时间】:2017-04-07 08:28:59 【问题描述】:我正在尝试使用批量发送 API 端点根据他们的兴趣向特定用户发送一些推送通知。我们得出结论,通知中心的标签系统无法提供我们需要的灵活性。
我有一个存储设备令牌的数据库,该设备令牌可供直接发送端点使用,但即使使用 Microsoft 提供的标准信息,也会提供 403 错误,而没有关于问题所在的信息。
public class WnsNotificationService : BaseNotificationService
private ISubscriptionProvider subscriptionProvider;
/// <summary>
/// Initializes a new instance of the <see cref="WnsNotificationService" /> class.
/// </summary>
/// <param name="telemetryService">The telemetry service.</param>
/// <param name="subscriptionProvider">The subscription provider.</param>
public WnsNotificationService(ITelemetryService telemetryService, ISubscriptionProvider subscriptionProvider) : base(telemetryService)
this.subscriptionProvider = subscriptionProvider;
/// <summary>
/// Sends the new Airport Event notification to windows devices via WNS.
/// </summary>
/// <param name="airportEvent">The Airport Event.</param>
/// <returns>
/// Notification Result
/// </returns>
public override async Task<NotificationResult> SendNotification(AirportEvent airportEvent)
try
IEnumerable<IEnumerable<string>> deviceCollection =
this.subscriptionProvider.GetSubscribedUserPNSHandles(airportEvent, PushNotificationPlatform.wns).Batch(1000);
foreach (var devices in deviceCollection)
ServiceBusConnectionStringBuilder connectionString = new ServiceBusConnectionStringBuilder(ConfigurationManager.AppSettings["Microsoft.Azure.NotificationHubs.ConnectionString"]);
string serviceBusNamespace = connectionString.Endpoints.First().Host;
string namespaceKeyName = connectionString.SharedAccessKeyName;
string namespaceKey = connectionString.SharedAccessKey;
var uri = new Uri($" ConfigurationManager.AppSettings["Microsoft.Azure.NotificationHubs.Url"] / ConfigurationManager.AppSettings["Microsoft.Azure.NotificationHubs.HubName"] /messages/$batch?direct&api-version=2015-08");
var request = WebRequest.CreateHttp(uri);
request.Method = "POST";
request.ContentType = @"multipart/mixed; boundary = ""simple-boundary""";
request.Headers["Authorization"] = SharedAccessSignatureTokenProvider.GetSharedAccessSignature(namespaceKeyName, namespaceKey, serviceBusNamespace, TimeSpan.FromMinutes(45));
request.Headers["ServiceBusNotification-Format"] = "windows";
request.Headers["X-WNS-Type"] = "wns/raw";
string body = this.GenerateBatchRequestBody(airportEvent.AirportEventId, devices);
byte[] requestBytes = new ASCIIEncoding().GetBytes(body);
Stream requestStream = request.GetRequestStream();
requestStream.Write(requestBytes, 0, requestBytes.Length);
request.GetResponse();
return new NotificationResult(true);
catch (Exception)
return new NotificationResult(false);
/// <summary>
/// Sends the new Airport Event notification to a specific windows device via WNS.
/// </summary>
/// <param name="deviceId">The device identifier.</param>
/// <param name="airportEvent">The Airport Event.</param>
/// <returns>
/// Notification Result
/// </returns>
/// <exception cref="System.NotImplementedException">Not Implemented Exception</exception>
public override Task<NotificationResult> SendNotificationToDevice(string deviceId, AirportEvent airportEvent)
throw new NotImplementedException();
/// <summary>
/// Generates the batch request body.
/// </summary>
/// <param name="airportEventGuid">The airport event unique identifier.</param>
/// <param name="devices">The devices.</param>
/// <returns>Request Body</returns>
private string GenerateBatchRequestBody(Guid airportEventGuid, IEnumerable<string> devices)
return @"
--simple-boundary
Content-type: text/xml
Content-Disposition: inline; name=notification
<toast><visual><binding template=""ToastText01""><text id=""1"">Hello from
Batch Direct Send!</text></binding></visual></toast>
--simple-boundary
Content-type: application/json
Content-Disposition: inline; name=devices
['https://foo.notify.windows.com/?token=bar']
--simple-boundary--";
这是使用https://azure.microsoft.com/en-us/blog/push-notification-hubs-batch-direct-send/提供的信息
【问题讨论】:
【参考方案1】:根据您的描述,我检查了Direct Batch Send 并按照此示例azure-notificationhubs-samples 在我这边测试了这个问题。经过一些试验,我可以让它在我身边工作,如下所示:
根据您的代码,我假设您需要将request.Headers["X-WNS-Type"] = "wns/raw";
更改为request.Headers["X-WNS-Type"] = "wns/toast";
,并且您的授权令牌可能无效。这是我生成授权令牌的方法,您可以参考:
/// <summary>
/// GetSharedAccessSignature
/// </summary>
/// <param name="SasKeyName">SharedAccessKeyName</param>
/// <param name="SasKeyValue">SharedAccessKey</param>
/// <param name="uri">resourceURI (e.g. https://namespace.servicebus.windows.net/NotificationHub/messages/$batch?direct&api-version=2015-08)</param>
/// <param name="minUntilExpire">minUntilExpire</param>
/// <returns></returns>
private static string GetSharedAccessSignature(string SasKeyName, string SasKeyValue, string uri, TimeSpan minUntilExpire)
string targetUri = Uri.EscapeDataString(uri.ToLower()).ToLower();
// Add an expiration in seconds to it.
long expiresOnDate = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
expiresOnDate += (long)minUntilExpire.TotalMilliseconds;
long expires_seconds = expiresOnDate / 1000;
String toSign = targetUri + "\n" + expires_seconds;
// Generate a HMAC-SHA256 hash or the uri and expiration using your secret key.
byte[] keyBytes = System.Text.Encoding.UTF8.GetBytes(SasKeyValue);
HMACSHA256 hmacsha256 = new HMACSHA256(keyBytes);
byte[] hash = hmacsha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(toSign));
// Create the token string using the base64
string signature = Uri.EscapeDataString(Convert.ToBase64String(hash));
return "SharedAccessSignature sr=" + targetUri + "&sig=" + signature + "&se=" + expires_seconds + "&skn=" + SasKeyName;
注意:这里是官方代码示例,可以参考it。
此外,Direct Batch Send 声明如下:
将一批通知直接发送到设备句柄集合(由 Notification 类型表示的有效令牌)。此 API 适用于 Basic and Standard tier Notification Hub namespaces。
您需要将定价层更改为基本或标准,否则您将通过Fiddler 收到以下回复:
【讨论】:
太棒了,感谢布鲁斯的全面回答!以上是关于尝试使用 Azure 通知中心批量发送时出现 403 禁止的主要内容,如果未能解决你的问题,请参考以下文章
尝试发送 100 MB 批处理时出现 Azure 服务总线错误
从 Azure Blob 存储批量插入时出现 ERRORFILE 问题