Registrazione dal back-end dell'app

Come accennato nelle sezioni precedenti, i dispositivi devono creare una o più registrazioni in un hub di notifica per ricevere notifiche push. Un modo per completare questa registrazione consiste nel chiedere ai dispositivi mobili di contattare direttamente l'hub di notifica per specificare (o aggiornare) il relativo handle PNS e i relativi tag. Questo approccio presenta alcune limitazioni e esistono alcuni scenari in cui è consigliabile contattare il back-end dell'app quando un dispositivo aggiorna la registrazione. Il back-end chiama quindi l'hub di notifica.

Quando eseguire la registrazione dal back-end dell'app

Esistono due scenari in cui è consigliabile instradare le registrazioni dei dispositivi tramite il back-end dell'app.

I tag devono essere protetti

Quando un dispositivo viene registrato direttamente con un hub di notifica, può specificare qualsiasi tag desiderato. Questo non è un problema se i tag sono gruppi di interesse pubblico a cui qualsiasi dispositivo può sottoscrivere (ad esempio, notizie relative alle squadre sportive). Tuttavia, questo può essere un problema quando alcuni tag sono disponibili solo per alcuni utenti.

Per registrare ogni utente solo ai tag consentiti, è necessario instradare le operazioni di registrazione tramite il back-end dell'app, che può eseguire l'autenticazione utente e autorizzare la registrazione per i tag necessari.

L'esempio più comune di questo scenario è l'uso di tag per rappresentare gli ID utente. In questo caso, si vuole impedire ai dispositivi di eseguire la registrazione ai tag che rappresentano altri utenti, in quanto riceveranno le notifiche di tale altro utente.

I tag vengono modificati dal back-end dell'app

La registrazione dal dispositivo è pratica e consente di configurare rapidamente le notifiche push e il routing avanzato ai gruppi di interesse. Tuttavia, la registrazione dal dispositivo non funziona molto bene se si desidera modificare i tag in seguito a eventi che si verificano in altri dispositivi.

Si considerino due scenari: se i tag nel telefono di Alice vengono impostati come risultato di eventi che si verificano nel telefono di Alice, è facile per l'app aggiornare i tag nell'hub di notifica. Se, d'altra parte, i tag devono cambiare in seguito a eventi che si verificano su altri dispositivi (ad esempio, il portatile di Alice quando si è connessi a un sito Web), il dispositivo dovrà attendere che l'app sia nuovamente attiva per riflettere le modifiche nell'hub di notifica.

Un esempio specifico dello scenario precedente è un'app musicale che include un'esperienza Web e un'app per dispositivi mobili. In questo caso, un utente specifico potrebbe seguire una nuova banda tramite il sito Web e vuole che il dispositivo inizi a ricevere notifiche sul nuovo gruppo il prima possibile. Un altro esempio è quando i tag provengono da altre parti del back-end (ad esempio, un CRM), che può modificare lo stato dell'utente da Silver a Gold. Questa modifica può comportare l'impostazione di un nuovo tag in tutte le registrazioni degli utenti.

Come eseguire la registrazione dal back-end

Quando si registra un dispositivo, un hub di notifica deve distinguere tra dispositivi diversi. Questa operazione non può essere eseguita solo esaminando gli handle PNS, perché sono temporanei e non univoci. Per risolvere questo problema, Hub di notifica genera ID di registrazione di lunga durata che ogni dispositivo deve archiviare localmente per poter fare riferimento alla propria registrazione ogni volta che aggiorna il relativo handle PNS, tag o modello.

La figura seguente illustra il flusso di registrazione per le notifiche native:

  1. Nel dispositivo, se non viene archiviato alcun ID di registrazione in locale,

    1. Chiamare il back-end dell'app per ottenere l'ID registrazione.

    2. Il back-end dell'app chiama hub di notifica per creare un nuovo ID di registrazione e quindi restituire l'ID al dispositivo.

    3. Archiviare l'ID registrazione nell'archiviazione locale del dispositivo.

  2. Nel dispositivo recuperare l'ID registrazione dall'archiviazione locale:

    1. Chiamare il back-end dell'app, specificando l'ID di registrazione, l'handle PNS e i tag.

    2. Il back-end dell'app crea o aggiorna la registrazione corrispondente nell'hub di notifica.

    3. Se il back-end dell'app restituisce il codice di stato 410, è necessario creare un nuovo ID di registrazione. Eliminare l'ID registrazione dalla risorsa di archiviazione locale e riavviare il passaggio 1.

Backend Registration

Il flusso per le notifiche del modello è analogo. Le uniche differenze sono le seguenti:

  1. Se un dispositivo usa più modelli, deve archiviare un ID di registrazione per ogni modello.

  2. È possibile identificare i modelli usando la proprietà TemplateName della registrazione.

Il codice seguente è un esempio di endpoint back-end.

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());
            }
        }
    }

Si noti che nel codice precedente è necessario aggiungere la logica per assicurarsi che il client che chiama tale endpoint sia autorizzato a registrarsi per i tag richiesti. Inoltre, il back-end può aggiungere i tag stessi (ad esempio, un tag userid ).

L'esempio di codice seguente illustra come implementare il metodo di registrazione per un'app Windows Store, dal dispositivo, con gli endpoint precedenti:

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"];

        }
    }

Archiviazione degli ID di registrazione in un database back-end

In alcuni casi, le applicazioni vogliono mantenere gli ID di registrazione nel back-end dell'app anziché nell'archiviazione locale del dispositivo. Ciò si verifica in genere quando il back-end dell'app ha già un modo per identificare i dispositivi (ad esempio, un id di installazione) e un modo per archiviare le informazioni sul dispositivo nell'archiviazione back-end( ad esempio, quando si esegue la migrazione da una soluzione push personalizzata in cui sono stati archiviati gli handle PNS).

Come modificare i tag dal back-end

Se si desidera modificare i tag dal back-end, è necessario disporre di un modo per consentire al back-end di identificare le registrazioni da modificare. Questa operazione viene in genere eseguita usando un tag.

Si supponga, ad esempio, che sia presente un'app musicale in cui un utente aggiunge una nuova band preferita dal Web e il back-end aggiunge un tag nelle registrazioni per dispositivi mobili dell'utente in seguito. In questo caso, l'app usa un tag per identificare l'utente e quindi usa tale tag per recuperare le registrazioni da aggiornare e aggiornarle.

Nell'esempio di codice seguente vengono recuperate le registrazioni e viene aggiunto un nuovo tag.

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

Si noti che in questo esempio, se si usano modelli, si presuppone che si stia aggiungendo il tag in tutti i modelli.