Registrando-se no back-end do aplicativo

Conforme mencionado nas seções anteriores, os dispositivos devem criar um ou mais registros em um hub de notificação para receber notificações por push. Uma maneira de concluir esse registro é fazer com que os dispositivos móveis entrem em contato diretamente com o hub de notificação para especificar (ou atualizar) seu identificador PNS e suas marcas. Essa abordagem tem várias limitações e há alguns cenários em que é recomendável entrar em contato com seu próprio back-end de aplicativo quando um dispositivo atualiza seu registro. O back-end então chama o hub de notificação.

Quando se registrar no back-end do aplicativo

Há dois cenários em que é recomendável rotear registros de dispositivo por meio do back-end do aplicativo.

As marcas devem ser protegidas

Quando um dispositivo se registra diretamente com um hub de notificação, ele pode especificar qualquer marca desejada. Isso não será um problema se as marcas forem grupos de interesse público aos quais qualquer dispositivo pode assinar (por exemplo, feeds de notícias sobre equipes esportivas). No entanto, isso pode ser um problema quando algumas marcas estão disponíveis apenas para alguns usuários.

Para registrar cada usuário somente para as marcas permitidas, você deve rotear as operações de registro por meio de seu próprio back-end de aplicativo, que pode executar a autenticação do usuário e autorizar o registro para as marcas necessárias.

O exemplo mais comum desse cenário é usar marcas para representar IDs de usuário. Nesse caso, você deseja impedir que os dispositivos se registrem em marcas que representam outros usuários, pois eles receberiam as notificações desse outro usuário.

As marcas são modificadas pelo back-end do aplicativo

O registro do dispositivo é conveniente e permite configurar rapidamente notificações por push e roteamento avançado para grupos de interesse. No entanto, o registro do dispositivo não funcionará muito bem se você quiser alterar marcas como resultado de eventos que ocorrem em outros dispositivos.

Considere dois cenários: se as marcas no telefone de Alice forem definidas como resultado de eventos que ocorrem no telefone de Alice, então é fácil para o aplicativo atualizar as marcas no hub de notificação. Se, por outro lado, as marcas precisarem ser alteradas como resultado de eventos que ocorrem em outros dispositivos (por exemplo, o laptop de Alice quando conectado a um site), o dispositivo terá que aguardar o aplicativo estar ativo novamente para refletir as alterações no hub de notificação.

Um exemplo específico do cenário anterior é um aplicativo de música que inclui uma experiência da Web e um aplicativo móvel. Nesse caso, um usuário específico pode seguir uma nova banda por meio do site e desejar que o dispositivo comece a receber notificações sobre a nova banda assim que possível. Outro exemplo é quando as marcas vêm de outras partes do back-end (um CRM, por exemplo), que podem alterar o status do usuário de Prata para Ouro. Essa alteração pode resultar na definição de uma nova marca nos registros de todos os usuários.

Como se registrar no back-end

Ao registrar um dispositivo, um hub de notificação deve distinguir entre dispositivos diferentes. Isso não pode ser feito apenas examinando as alças PNS, pois elas são transitórias e não são exclusivas. Para resolver esse problema, os Hubs de Notificação geram IDs de registro de longa duração que cada dispositivo deve armazenar localmente para poder consultar seu próprio registro sempre que atualizar seu identificador PNS, marcas ou modelo.

A figura a seguir mostra o fluxo de registro para notificações nativas:

  1. No dispositivo, se nenhuma ID de registro for armazenada localmente,

    1. Chame o back-end do aplicativo para obter a ID de registro.

    2. O back-end do aplicativo chama hubs de notificação para criar uma nova ID de registro e, em seguida, retornar a ID de volta ao dispositivo.

    3. Armazene a ID de registro no armazenamento local do dispositivo.

  2. No dispositivo, recupere a ID de registro do armazenamento local:

    1. Chame o back-end do aplicativo, fornecendo a ID de registro, o identificador PNS e as marcas.

    2. O back-end do aplicativo cria ou atualiza o registro correspondente no hub de notificação.

    3. Se o back-end do aplicativo retornar o código de status 410, uma nova ID de registro deverá ser criada. Exclua a ID de registro do armazenamento local e reinicie da etapa 1.

Backend Registration

O fluxo de notificações de modelo é análogo. As únicas diferenças são as seguintes:

  1. Se um dispositivo usa vários modelos, ele deve armazenar uma ID de registro por modelo.

  2. Você pode identificar os modelos usando a propriedade TemplateName do registro.

O código a seguir é um exemplo de pontos de extremidade de 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());
            }
        }
    }

Observe que, no código anterior, você deve adicionar a lógica para garantir que o cliente que está chamando esse ponto de extremidade esteja autorizado a se registrar para as marcas solicitadas. Além disso, o back-end pode adicionar as próprias marcas (por exemplo, uma marca userid ).

O exemplo de código a seguir mostra como implementar o método de registro para um aplicativo da Windows Store, do dispositivo, com os pontos de extremidade anteriores:

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

        }
    }

Armazenar IDs de registro em um banco de dados de back-end

Às vezes, os aplicativos desejam manter as IDs de registro no back-end do aplicativo em vez de no armazenamento local do dispositivo. Isso geralmente acontece quando o back-end do aplicativo já tem uma maneira de identificar dispositivos (por exemplo, uma instalaçãoId) e uma maneira de armazenar informações do dispositivo no armazenamento de back-end (por exemplo, ao migrar de uma solução de push personalizada na qual os identificadores PNS foram armazenados).

Como modificar marcas do back-end

Se você quiser modificar marcas do back-end, deverá ter uma maneira de o back-end identificar os registros que devem ser modificados. Isso geralmente é feito usando uma marca.

Por exemplo, suponha que haja um aplicativo de música no qual um usuário adiciona uma nova banda favorita da Web, e o back-end adiciona uma marca nos registros móveis do usuário como resultado disso. Nesse caso, o aplicativo usa uma marca para identificar o usuário e, em seguida, usa essa marca para recuperar os registros a serem atualizados e atualizá-los.

O exemplo de código a seguir recupera os registros e adiciona uma nova marca a eles.

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

Observe que, neste exemplo, se você estiver usando modelos, presumimos que você esteja adicionando a marca em todos os seus modelos.