티스토리 뷰

반응형

[Azure Mobile Service] Raw Push Notification, Local Database Sync

 

이 방대한 작업 작은 이유에서 시작되었다.

즐겨 찾기 구현과와 최근 항목 구현하기.. 어느날 이 두가지를 구현해야지~하는 생각으로 작업을 시작했는데.. 처음에는 단순하게 Roaming Storage를 이용해서 공유를 하면 되겠구나..라고 생각했다. 그래서 이레 저레 작업이 대충 마무리가 되어서 테스트를 했는데..윈폰과 윈8 사이의 싱크가 바로 바로 되지 않는다..

 

그래서, 그 때가지 작업 했던 내용을 주석처리하고, AMS에 즐겨찾기, 최근항목을 저장하고, 추가/삭제시 Raw Push Notification을 발생 시켜서 윈폰과 윈8간에 동기화를 하도록 계획을 변경했다.

 

음 그래서 이런 저런 내용을 작업하다보니..거의 일주일? 정도 걸린듯하다..쿨럭

 

 

1. 참고자료

 

종인님 코맨트

https://www.facebook.com/groups/vapps/

 

알림 허브 개요

http://msdn.microsoft.com/ko-kr/library/jj927170.aspx

 

Getting Started with Notification Hubs

http://azure.microsoft.com/en-us/documentation/articles/notification-hubs-windows-store-dotnet-get-started/

 

 

 

2. .Net Backend 에서 Raw Push Notification 보내기

 

이미 내가 만들어 놓은 서비스에서 간단하게 Push Notification을 사용하고 있었다.

 

 

        // POST tables/HstCommentDTO
        public async Task<IHttpActionResult> PostHstCommentDTO(HstCommentDTO item)
        {
            HstCommentDTO current = await InsertAsync(item);
            //프로시저 실행
            ExecUpdate_UserAndFolderTotalInformation(item.FolderId);

            //Push 대상, 폴더 등록자, 코맨트에 부모가 있을 경우 부모 등록자, 두경우 모두 등록자 자신이면 제외

            // Create a WNS native toast.
            var message = new WindowsPushMessage
            {
                XmlPayload = @"<?xml version=""1.0"" encoding=""utf-8""?>" +
                             @"<toast><visual><binding template=""ToastText01"">" +
                             @"<text id=""1"">" + item.ValueDesc + @"</text>" +
                             @"</binding></visual></toast>"
            };

            // Define the XML paylod for a WNS native toast notification
            // that contains the text of the inserted item.
            try
            {
                var currentUser = User as ServiceUser;
                if (currentUser != null)
                {
                    var result = await Services.Push.SendAsync(message, string.Format("!{0}", currentUser.Id));
                    Services.Log.Info(result.State.ToString());
                }
            }
            catch (System.Exception ex)
            {
                Services.Log.Error(ex.Message, null, "Push.SendAsync Error");
            }

            return CreatedAtRoute("Tables", new {id = current.Id}, current);
        }

 

 

기본 예제를 이용해서 만들다 보면 작성하는 코드로 특별한 내용은 없다. 그런데, 알림 허브 개요(Notification Hubs Overview) 페이지를 보니 이렇게 보내는 Push는 아래의 그림과 같은 구조인 듯하다.

 

 

 

.Net Backend service에서 직접 PNS 서버에 Push를 요청하는 구조이며, Raw Push Notification을 요청하는 소스는 아래와 같다.

 

 

        private async void RawPushNotification(string tags, string type, string action, string data)

        {
            if (string.IsNullOrEmpty(tags) || string.IsNullOrEmpty(type) || string.IsNullOrEmpty(action) || string.IsNullOrEmpty(data)) return;

 

            //Raw push notification data create
            var datas = new Dictionary<string, string> { { "type", type }, { "action", action }, { "data", data } };
            var message = new WindowsPushMessage
            {
                XmlPayload = JsonConvert.SerializeObject(datas)
            };
            message.Headers.Add("X-WNS-Type", "wns/raw");
            try
            {
                var result = await Services.Push.SendAsync(message, tags);
                Services.Log.Info(result.State.ToString());
            }
            catch (System.Exception ex)
            {
                // ReSharper disable once ExplicitCallerInfoArgument
                Services.Log.Error(ex.Message, null, string.Format("tags:{0},type:{1},action:{2},data:{3}", tags, type, action, data));
            }
        }

 

 

대충 이런 메소드를 각 컨트롤러에 넣어 놓고 보내는 연습을 했는데 잘 동작하는 하는 듯해서..넘어가려고 했는데, 종인님이 알려준 내용을 다시 찾아보니,

 

 

 

이런 구조를 가지도록 만드는 것이 더 좋다는 것을 알게되었다. 으흠.. 그래서, 다시 아래 내용 처럼 PushHelper를 만들게 되었다.

 

 

    public class PushHelper
    {
        private static readonly NotificationHubClient HubClient = NotificationHubClient.CreateClientFromConnectionString
            (
                "<full-access endpoint>",
                "<hub name>");

        /// <summary>
        /// RawPushNotification
        /// </summary>
        /// <returns></returns>
        public static async Task RawPushNotification(string tags, string type, string action, string data)
        {
            if (string.IsNullOrEmpty(tags) || string.IsNullOrEmpty(type) || string.IsNullOrEmpty(action) ||
                string.IsNullOrEmpty(data)) return;

            //Raw push notification data
            var datas = new Dictionary<string, string> {{"type", type}, {"action", action}, {"data", data}};
            string payload = JsonConvert.SerializeObject(datas);
            var message = new WindowsNotification(payload, new Dictionary<string, string>
            {
                {"X-WNS-Type", "wns/raw"}
            });
            try
            {
                NotificationOutcome result = await HubClient.SendNotificationAsync(message, tags);
                if (result.Results != null && result.Results.Any())
                {
                }
                //Services.Log.Info(result.State.ToString());
            }
            catch (Exception ex)
            {
                // ReSharper disable once ExplicitCallerInfoArgument
                //Services.Log.Error(ex.Message, null, string.Format("tags:{0},type:{1},action:{2},data:{3}", tags, type, action, data));
                Debug.WriteLine(ex.Message);
            }
        }
    }

 

대략 Push만 가능한 상태의 Push 전용 메소드이다. 약간 아쉬운것은 Log를 남기는 방법을 아직 몰라서 로그를 남길 수 없다는 것이다.

 

이렇게 전용 메소드를 만들고, 각 컨트롤러도 수정을 했다.

 

 

        // POST tables/HstUserFavoriteDTO
        public async Task<IHttpActionResult> PostHstUserFavoriteDTO(HstUserFavoriteDTO item)
        {
            HstUserFavoriteDTO current = await InsertAsync(item);
            var currentUser = User as ServiceUser;
            await PushHelper.RawPushNotification(currentUser.Id, "HstUserFavoriteDTO", "POST", item.Id);
            return CreatedAtRoute("Tables", new {id = current.Id}, current);
        }

        // DELETE tables/HstUserFavoriteDTO/48D68C86-6EA6-4C25-AA33-223FC9A27959
// ReSharper disable once InconsistentNaming
        public async Task DeleteHstUserFavoriteDTO(string id)
        {
            //삭제할 아이템의 존재 여부 확인, 존재하지 않으면 삭제하지 않음, 노티도 발생시키지 않음
            SingleResult<HstUserFavoriteDTO> deleteItem = Lookup(id);
            if (!deleteItem.Queryable.Any()) return;

            await DeleteAsync(id);
            var currentUser = User as ServiceUser;
            await PushHelper.RawPushNotification(currentUser.Id, "HstUserFavoriteDTO", "DELETE", id);
            //return DeleteAsync(id);
        }

 

 

PushHelper.RawPushNotification 메소드 호출은 반드시 await를 붙여 주어야한다. 그러다 보니, Delete 웹 메소드 부분이 여러가지 변화가 있는데, async로 변경을 하고, DeleteAsync(id)와 PushHelper.RawPushNotification 모두 await를 붙여 주는 것이다.

 

 

앞으로도 PushHelper는 계속 이런 저런 기능을 추가해 나가야 할 것 같다.

 

반응형
댓글