앱 백 엔드에서 등록

이전 섹션에서 설명한 것처럼 디바이스는 푸시 알림을 받으려면 알림 허브에서 하나 이상의 등록을 만들어야 합니다. 이 등록을 완료하는 한 가지 방법은 모바일 디바이스가 알림 허브에 직접 연결하여 PNS 핸들과 태그를 지정(또는 업데이트)하도록 하는 것입니다. 이 방법은 여러 가지 제한 사항이 있으며 디바이스가 등록을 새로 고칠 때 사용자 고유의 앱 백 엔드에 문의하는 것이 좋습니다. 백 엔드는 알림 허브를 호출합니다.

앱 백 엔드에서 등록하는 경우

앱 백 엔드를 통해 디바이스 등록을 라우팅하는 것이 좋습니다.

태그를 보호해야 합니다.

디바이스가 알림 허브에 직접 등록되면 원하는 태그를 지정할 수 있습니다. 태그가 모든 디바이스가 구독할 수 있는 공익 그룹인 경우(예: 스포츠 팀에 관한 뉴스 피드) 문제가 되지 않습니다. 그러나 일부 태그는 일부 사용자만 사용할 수 있는 경우 문제가 될 수 있습니다.

각 사용자를 허용된 태그에만 등록하려면 사용자 인증을 수행하고 필요한 태그에 대한 등록 권한을 부여할 수 있는 사용자 고유의 앱 백 엔드를 통해 등록 작업을 라우팅해야 합니다.

이 시나리오의 가장 일반적인 예는 태그를 사용하여 사용자 ID를 나타내는 것입니다. 이 경우 디바이스가 다른 사용자의 알림을 받는 것처럼 다른 사용자를 나타내는 태그에 등록하지 못하도록 방지하려고 합니다.

태그는 앱 백 엔드에 의해 수정됩니다.

디바이스에서 등록하면 편리하며 관심 그룹에 대한 푸시 알림 및 풍부한 라우팅을 신속하게 설정할 수 있습니다. 그러나 다른 디바이스에서 발생하는 이벤트의 결과로 태그를 변경하려는 경우 디바이스에서 등록하는 것이 잘 작동하지 않습니다.

두 가지 시나리오를 고려합니다. Alice의 휴대폰에서 발생하는 이벤트의 결과로 Alice 휴대폰의 태그가 설정된 경우 앱이 알림 허브에서 태그를 쉽게 업데이트할 수 있습니다. 반면에 다른 디바이스에서 발생하는 이벤트(예: 웹 사이트에 로그온할 때 Alice의 노트북)로 인해 태그가 변경되어야 하는 경우 디바이스는 알림 허브의 변경 내용을 반영하기 위해 앱이 다시 활성화될 때까지 기다려야 합니다.

이전 시나리오의 특정 예는 웹 환경과 모바일 앱을 포함하는 음악 앱입니다. 이 경우 특정 사용자가 웹 사이트를 통해 새 밴드를 팔로우하고 디바이스가 가능한 한 빨리 새 밴드에 대한 알림을 받기 시작하도록 할 수 있습니다. 또 다른 예는 태그가 백 엔드의 다른 부분(예: CRM)에서 오는 경우로, 사용자의 상태를 Silver에서 Gold로 변경할 수 있습니다. 이 변경으로 인해 모든 사용자의 등록에 새 태그가 설정될 수 있습니다.

백 엔드에서 등록하는 방법

디바이스를 등록할 때 알림 허브는 서로 다른 디바이스를 구분해야 합니다. 일시적이고 고유하지 않으므로 PNS 핸들을 보는 것만으로는 이 작업을 수행할 수 없습니다. 이 문제를 해결하기 위해 Notification Hubs는 PNS 핸들, 태그 또는 템플릿을 업데이트할 때마다 자체 등록을 참조할 수 있도록 각 디바이스가 로컬에 저장해야 하는 장수 등록 ID를 생성합니다.

다음 그림에서는 네이티브 알림에 대한 등록 흐름을 보여 줍니다.

  1. 디바이스에서 등록 ID가 로컬로 저장되지 않은 경우

    1. 앱 백 엔드를 호출하여 등록 ID를 가져옵니다.

    2. 앱 백 엔드는 알림 허브를 호출하여 새 등록 ID를 만든 다음, ID를 디바이스로 다시 반환합니다.

    3. 디바이스 로컬 스토리지에서 등록 ID를 Microsoft Store.

  2. 디바이스에서 로컬 스토리지에서 등록 ID를 검색합니다.

    1. 등록 ID, PNS 핸들 및 태그를 제공하여 앱 백 엔드를 호출합니다.

    2. 앱 백 엔드는 알림 허브에서 해당 등록을 만들거나 업데이트합니다.

    3. 앱 백 엔드가 상태 코드 410을 반환하는 경우 새 등록 ID를 만들어야 합니다. 로컬 스토리지에서 등록 ID를 삭제하고 1단계에서 다시 시작합니다.

Backend Registration

템플릿 알림의 흐름은 유사합니다. 유일한 차이점은 다음과 같습니다.

  1. 디바이스에서 여러 템플릿을 사용하는 경우 템플릿당 하나의 등록 ID를 저장해야 합니다.

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

        }
    }

백 엔드 데이터베이스에 등록 ID 저장

경우에 따라 애플리케이션은 디바이스 로컬 스토리지 대신 앱 백 엔드에 등록 ID를 유지하려고 합니다. 이는 일반적으로 앱 백 엔드가 디바이스를 식별하는 방법(예: installationId)과 백 엔드 스토리지에 디바이스 정보를 저장하는 방법(예: PNS 핸들이 저장된 사용자 지정 푸시 솔루션에서 마이그레이션하는 경우)이 이미 있는 경우에 발생합니다.

백 엔드에서 태그를 수정하는 방법

백 엔드에서 태그를 수정하려면 백 엔드에서 수정할 등록을 식별하는 방법이 있어야 합니다. 이 작업은 일반적으로 태그를 사용하여 수행됩니다.

예를 들어 사용자가 웹에서 새 즐겨찾기 밴드를 추가하는 음악 앱이 있다고 가정하고 백 엔드는 그 결과로 사용자의 모바일 등록에 태그를 추가합니다. 이 경우 앱은 태그를 사용하여 사용자를 식별한 다음 해당 태그를 사용하여 업데이트할 등록을 검색하고 업데이트합니다.

다음 코드 예제에서는 등록을 검색하고 새 태그를 추가합니다.

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

이 예제에서는 템플릿을 사용하는 경우 모든 템플릿에 태그를 추가한다고 가정합니다.