Share via


從應用程式後端註冊

如前幾節所述,裝置必須在通知中樞中建立一或多個註冊,才能接收推播通知。 完成此註冊的其中一種方法是讓行動裝置直接連絡通知中樞,以指定 (或更新其 PNS 控制碼及其標籤) 。 此方法有一些限制,而且在某些情況下,建議您在裝置重新整理註冊時連絡您自己的應用程式後端。 後端接著會呼叫通知中樞。

從應用程式後端註冊的時機

在兩個案例中,建議您透過應用程式後端路由傳送裝置註冊。

必須保護標籤

當裝置直接向通知中樞註冊時,它可以指定想要的任何標籤。 如果標籤是任何裝置可訂閱 (的公用興趣群組,例如,有關運動團隊的新聞摘要) ,則這不是問題。 不過,當某些標籤僅適用于某些使用者時,這可能會發生問題。

若要只向允許的標籤註冊每個使用者,您必須透過自己的應用程式後端路由註冊作業,以便執行使用者驗證並授權註冊所需的標籤。

此案例最常見的範例是使用標記來代表使用者識別碼。 在此情況下,您想要防止裝置註冊代表其他使用者的標籤,因為它們會收到其他使用者的通知。

標籤是由您的應用程式後端修改

從裝置註冊很方便,可讓您快速設定推播通知,以及對興趣群組的豐富路由。 不過,如果您想要因其他裝置上發生的事件而變更標籤,則從裝置註冊無法正常運作。

請考慮兩種案例:如果 Alice 手機上的標籤設定為 Alice 手機上發生事件的結果,則應用程式很容易在通知中樞更新標籤。 如果另一方面,標籤必須因為其他裝置上發生的事件而變更 (,例如,Alice 的膝上型電腦登入網站) ,則裝置必須等候應用程式再次作用中,才能反映通知中樞中的變更。

上述案例的特定範例是包含 Web 體驗和行動應用程式的音樂應用程式。 在此情況下,特定使用者可能會透過網站追蹤新的訊號範圍,並想要裝置儘快開始接收新訊號範圍的相關通知。 另一個範例是標籤來自 CRM 的其他部分 (CRM,例如) ,可將使用者的狀態從 Silver 變更為 Gold。 這項變更可能會導致在所有使用者註冊上設定新的標記。

如何從後端註冊

註冊裝置時,通知中樞必須區分不同的裝置。 這無法只藉由查看 PNS 控制碼來完成,因為它們是暫時性且不是唯一的。 為了解決此問題,通知中樞會產生長期註冊識別碼,讓每個裝置都必須儲存在本機,才能在每次更新 PNS 控制碼、標記或範本時參考自己的註冊。

下圖顯示原生通知的註冊流程:

  1. 在裝置上,如果未在本機儲存任何註冊識別碼,則為

    1. 呼叫應用程式後端以取得註冊識別碼。

    2. 應用程式後端會呼叫通知中樞來建立新的註冊識別碼,然後將識別碼傳回給裝置。

    3. Microsoft Store裝置本機儲存體中的註冊識別碼。

  2. 在裝置上,從本機儲存體擷取註冊識別碼:

    1. 呼叫應用程式後端,提供註冊識別碼、PNS 控制碼和標記。

    2. 應用程式後端會在通知中樞上建立或更新對應的註冊。

    3. 如果應用程式後端傳回狀態碼 410,則必須建立新的註冊識別碼。 從本機儲存體刪除註冊識別碼,然後從步驟 1 重新開機。

Backend Registration

範本通知的流程類似。 唯一的差異如下:

  1. 如果裝置使用多個範本,則必須為每個範本儲存一個註冊識別碼。

  2. 您可以使用註冊的 TemplateName 屬性來識別範本。

下列程式碼是後端端點的範例。

public class RegisterController : ApiController
    {

        private NotificationHubClient hub;

        public RegisterController()
        {
            hub = NotificationHubClient.CreateClientFromConnectionString("Endpoint=sb://buildhub-ns.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=DuWV4SQ08poV6HZly8O/KQNWv3YRTZlExJxu3pNCjGU=", "build2014_2");
        }
        
        public class DeviceRegistration
        {
            public string Platform { get; set; }
            public string Handle { get; set; }
            public string[] Tags { get; set; }
        }

        // POST api/register
        // This creates a registration id
        public async Task<string> Post()
        {
            return await hub.CreateRegistrationIdAsync();
        }

        // PUT api/register/5
        // This creates or updates a registration (with provided PNS handle) at the specified id
        public async void Put(string id, DeviceRegistration deviceUpdate)
        {
            // IMPORTANT: add logic to make sure that caller is allowed to register for the provided tags
            
            RegistrationDescription registration = null;
            switch (deviceUpdate.Platform)
            {
                case "mpns":
                    registration = new MpnsRegistrationDescription(deviceUpdate.Handle);
                    break;
                case "wns":
                    registration = new WindowsRegistrationDescription(deviceUpdate.Handle);
                    break;
                case "apns":
                    registration = new AppleRegistrationDescription(deviceUpdate.Handle);
                    break;
                case "gcm":
                    registration = new GcmRegistrationDescription(deviceUpdate.Handle);
                    break;
                default:
                    throw new HttpResponseException(HttpStatusCode.BadRequest);
            }

            registration.RegistrationId = id;
            registration.Tags = new HashSet<string>(deviceUpdate.Tags);

            try
            {
                await hub.CreateOrUpdateRegistrationAsync(registration);
            } catch (MessagingException e) {
                ReturnGoneIfHubResponseIsGone(e);
            }
        }

        // DELETE api/register/5
        public async void Delete(string id)
        {
            await hub.DeleteRegistrationAsync(id);
        }


        private static void ReturnGoneIfHubResponseIsGone(MessagingException e)
        {
            var webex = e.InnerException as WebException;
            if (webex.Status == WebExceptionStatus.ProtocolError)
            {
                var response = (HttpWebResponse)webex.Response;
                if (response.StatusCode == HttpStatusCode.Gone)
                    throw new HttpRequestException(HttpStatusCode.Gone.ToString());
            }
        }
    }

請注意,在上述程式碼中,您必須新增邏輯,以確保呼叫該端點的用戶端有權註冊要求的標籤。 此外,您的後端可以新增標籤本身 (例如 userid 標籤) 。

下列程式碼範例示範如何使用上述端點,從裝置實作Windows Microsoft Store應用程式的註冊方法:

class RegisterClient
    {
        private string POST_URL = "{your back-end endpoints}";

        private class DeviceRegistration
        {
            public string Platform { get; set; }
            public string Handle { get; set; }
            public string[] Tags { get; set; }
        }

        public async Task RegisterAsync(string handle, IEnumerable<string> tags)
        {
            var regId = await RetrieveRegistrationIdOrRequestNewOneAsync();

            var deviceRegistration = new DeviceRegistration
            {
                Platform = "wns",
                Handle = handle,
                Tags = tags.ToArray<string>()
            };

            var statusCode = await UpdateRegistrationAsync(regId, deviceRegistration);

            if (statusCode == HttpStatusCode.Gone)
            {
                // regId is expired, deleting from local storage & recreating
                var settings = ApplicationData.Current.LocalSettings.Values;
                settings.Remove("__NHRegistrationId");
                regId = await RetrieveRegistrationIdOrRequestNewOneAsync();
                statusCode = await UpdateRegistrationAsync(regId, deviceRegistration);
            }

            if (statusCode != HttpStatusCode.Accepted)
            {
                // log or throw
            }
        }

        private async Task<HttpStatusCode> UpdateRegistrationAsync(string regId, DeviceRegistration deviceRegistration)
        {
            using (var httpClient = new HttpClient())
            {
                var putUri = POST_URL + "/" + regId;
                var response = await httpClient.PutAsJsonAsync<DeviceRegistration>(putUri, deviceRegistration);
                return response.StatusCode;
            }
        }

        private async Task<string> RetrieveRegistrationIdOrRequestNewOneAsync()
        {
            var settings = ApplicationData.Current.LocalSettings.Values;
            if (!settings.ContainsKey("__NHRegistrationId"))
            {
                using (var httpClient = new HttpClient())
                {
                    var response = await httpClient.PostAsync(POST_URL, new StringContent(""));
                    if (response.IsSuccessStatusCode)
                    {
                        string regId = await response.Content.ReadAsStringAsync();
                        regId = regId.Substring(1, regId.Length - 2);
                        settings.Add("__NHRegistrationId", regId);
                    }
                    else
                    {
                        throw new Exception();
                    }
                }
            }
            return (string)settings["__NHRegistrationId"];

        }
    }

將註冊識別碼儲存在後端資料庫中

有時候,應用程式想要將註冊識別碼保留在應用程式後端,而不是在裝置本機儲存體中。 例如,當應用程式後端已經能夠識別裝置 (例如 installationId) ,以及從 PNS 控制碼儲存) 的自訂推送解決方案移轉時,將裝置資訊儲存在後端儲存體 (時,通常會發生這種情況。

如何從後端修改標籤

如果您想要從後端修改標籤,則必須讓後端識別要修改的註冊。 這通常是使用 標記來完成。

例如,假設有一個音樂應用程式,其中使用者從 Web 新增了一個新的我的最愛的訊號,而後端會在使用者的行動註冊中新增標籤,因此。 在此情況下,應用程式會使用 標記來識別使用者,然後使用該標籤來擷取要更新並更新的註冊。

下列程式碼範例會擷取註冊,並將新的標記加入其中。

var registrations = await hub.GetRegistrationsByTagAsync("{userId}", 10);
            foreach (var reg in registrations)
            {
                reg.Tags.Add("{newBand}");
                await hub.UpdateRegistrationAsync(reg);
            }

請注意,在此範例中,如果您使用範本,我們假設您要在所有範本上新增 標籤。