uncategorized

Cordova用Web API註冊Azure Notification Hub

早些時候Cordova想要透過Azure Notification Hub做推播功能,可以透過azure-mobile-apps-js-clientphonegap-plugin-push套件,並搭配Azure Mobile app就可以輕鬆做到Push Message功能,不過,今日這樣組合已經無法幫Cordova在不寫程式情況下透過Azure Mobile App向Notification Hub註冊Device,主要是Azure Mobile App跟Notification Hub整合後的註冊device API似乎不見了,所以,使用上會出現下面錯誤

“Failed to load resource: the server responded with a status of 404 - Cannot GET /push/installations/xxxxx(it’s a guid string)”

因此,就必須在Mobile App上自己開發這一段API

安裝套件


要讓我們開發的API跟Azure Notification Hub做溝通,須安裝Microsoft.Azure.Notification套件,並建立一個Notification class來設定API與Notification Hub連線資訊

1
2
3
4
5
6
7
8
9
10
11
public class NotificationHub
{
public static NotificationHub Instance = new NotificationHub();
public NotificationHubClient Hub { get; set; }
private NotificationHub()
{
Hub = NotificationHubClient.CreateClientFromConnectionString("DefaultFullSharedAccessSignature","Notification Name");
}
}

  • DefaultFullSharedAccessSignature:在Notification Hub的Access Policies可以找到

  • Notification Name:就是你的Azure Notification Hub的名稱

    向Notification Hub註冊Device


幫你的Device註冊到Azure Notification Hub的資訊如下:

  • PNS識別碼:主要是要跟推播平台註冊的ID,每一個平台的編碼方式不同
  • 註冊ID:主要是跟zure Notification Hub註冊的識別碼
  • 標記:在這邊我們主要是定義pns識別碼的使用者識別,好讓我們後續能做個別推播的發送

其中,PNS註冊ID在當你APP被移除重新安裝時候,在Notification Hub上的這兩個資訊就會被更新

取得註冊ID

這邊先把Notification Hub通道建立起來

1
2
3
4
5
private NotificationHubClient hub;
public PushController()
{
hub = NotificationHub.Instance.Hub;
}

當Device註冊到Azure Notification Hub時,Notification Hub本身就會自動幫你建立一個Mapping表,透過下面程式可以列出所有註冊在Notification Hub上資訊,不過,有一點要注意,就是當你的Notification Hub在註冊推播平台的憑證換掉後,這些資訊似乎會消失

1
2
3
4
5
6
7
8
9
var allRegistrations = await hub.GetAllRegistrationsAsync(0);
var continuationToken = allRegistrations.ContinuationToken;
var registrationDescriptionsList = new List<RegistrationDescription>(allRegistrations);
while (!string.IsNullOrWhiteSpace(continuationToken))
{
var otherRegistrations = await hub.GetAllRegistrationsAsync(continuationToken, 0);
registrationDescriptionsList.AddRange(otherRegistrations);
continuationToken = otherRegistrations.ContinuationToken;
}

因此,為了避免同一個PNS會出現不同註冊ID,當需要取得註冊ID時候,可以把PNS碼帶入,如果有相同的PNS碼就會沿用當下的註冊ID,如果還是擔心,也可以加入Tag資訊做判斷

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[HttpPost]
public async Task<string> GetNhRegistrationid(string PNS = null)
{
string newNhRegistid = null;
if (PNS != null)
{
var registrations = await hub.GetRegistrationsByChannelAsync(PNS, 100);
foreach (RegistrationDescription registration in registrations)
{
if (newNhRegistid == null)
{
newNhRegistid = registration.RegistrationId;
}
else
{
await hub.DeleteRegistrationAsync(registration);
}
}
}
if (newNhRegistid == null)
{
newNhRegistid = await hub.CreateRegistrationIdAsync();
}
return newNhRegistid;
}

當有了註冊ID之後,接下來就是要註冊Device,所以,要建立註冊Device資訊的Class,這邊的Tag如果不是只有一組,可以使用陣列

1
2
3
4
5
6
public class DeviceRegist
{
public string Platform { get; set; }
public string PNS { get; set; }
public string Tags { get; set; }
}

其中,Platform簡稱是不能亂定義,資訊可以參考Notification Hub對於各個推播平台的簡稱

在deviceupdate.Platform中,會針對不同推播平台使用不同的方法去註冊PNS,而各大推播平台基本上也只認得PNS識別碼,所以,不僅要把註冊資訊註冊到Hub中,也必須把PNS註冊註冊到推播平台上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[HttpPut]
public async Task<HttpResponseMessage> RegisteredDevice(string inRegistrationId, DeviceRegist deviceupdate)
{
RegistrationDescription registration = null;
switch (deviceupdate.Platform)
{
case "apns":
registration = new AppleRegistrationDescription(deviceupdate.PNS);
break;
case "gcm":
registration = new GcmRegistrationDescription(deviceupdate.PNS);
break;
default:
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
registration.RegistrationId = inRegistrationId;
registration.Tags = new HashSet<string>();
registration.Tags.Add(deviceupdate.Tags);
try
{
await hub.CreateOrUpdateRegistrationAsync(registration);
}
catch (MessagingException ex)
{
ReturnGoneIfHubResponseIsGone(ex);
}
catch (Exception ex1)
{
throw ex1;
}
return Request.CreateResponse(HttpStatusCode.OK);
}

以上方式基本上就可以自行做到透過API將資訊註冊到Hub中,如果要想自己去刪掉推播平台上的PNS碼,可以自行加入下面這一段

1
2
3
4
5
6
[HttpDelete]
public async Task<HttpResponseMessage> Delete(string PNS)
{
await hub.DeleteRegistrationAsync(PNS);
return Request.CreateResponse(HttpStatusCode.OK);
}

此外,因為註冊資訊可能因為憑證被換掉或是更新導致Mapping被清掉,所以,這邊其實可以把這資訊存放到資料庫中,這樣就可以避免資訊消失,另外,因為PNS註冊ID這資訊格式基本上是固定,為了讓推播更有效率或是能分門別類的推播,就必須依賴Tag管理,利用Tag資訊去分門別類推播

這樣做完之後,Cordova要怎樣呼叫呢?我們依舊還是可以azure-mobile-apps-js-clientphonegap-plugin-push套件處理,只是不能採用原本文件上的寫法

1
2
3
4
push.on('registration', function (data) {
console.log('PNS data'+data.registrationId);
azureClient.push.register('gcm', data.registrationId);
});

這部分可以參考

Cordova 無法使用mobile service plugin結合azure PushNotification 的替代方案

這篇的解法