Поделиться через


Краткое руководство. Отправка push-уведомлений (XAML)

Облачный сервер может отправлять push-уведомление приложению через службы push-уведомлений Windows (WNS). Эта процедура применяется к плитке, всплывающему элементу, значку и необработанным push-уведомлениям.

Цель. Создание и отправка плитки, всплывающего уведомления, значка или необработанного push-уведомления.

Необходимые компоненты

Чтобы понять этот раздел или использовать указанный код, вам потребуется:

Instructions

1. Включите необходимые ссылки на пространство имен

Примеры, приведенные в этом разделе, можно использовать как есть, но требуют, чтобы код включал следующие ссылки на пространство имен:

using System.Net;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Web;
using System.Text;

2. Создание HTTP-запроса POST

Параметр uri — это универсальный код ресурса канала (URI), запрашиваемый приложением, и передается на облачный сервер. Дополнительные сведения см. в статье "Как запрашивать, создавать и сохранять канал уведомлений".

HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
request.Method = "POST";

3. Добавление обязательных заголовков

Существует четыре обязательных заголовка, которые должны быть включены во все push-уведомления: X-WNS-Type, Content-Type, Content-Length и Authorization.

  • Заголовок X-WNS-Type указывает, является ли это плитка, всплывающее уведомление, индикатор событий или необработанное уведомление.
  • Тип контента задается в зависимости от значения X-WNS-Type.
  • Длина содержимого дает размер включенных полезных данных уведомлений.
  • Заголовок авторизации указывает учетные данные проверки подлинности, которые позволяют отправлять push-уведомление этому пользователю через этот канал.

Параметр accessToken заголовка авторизации указывает маркер доступа, хранящийся на сервере, который был получен от WNS при запросе проверки подлинности облачного сервера. Без маркера доступа уведомление будет отклонено.

Полный список возможных заголовков см. в разделе "Запрос службы push-уведомлений" и заголовки ответа.

request.Headers.Add("X-WNS-Type", notificationType);
request.ContentType = contentType;
request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken.AccessToken));

4. Добавление подготовленного содержимого

Что касается HTTP-запроса, xml-содержимое уведомления представляет собой большой двоичный объект данных в тексте запроса. Например, не выполняется проверка соответствия XML спецификации X-WNS-Type. Содержимое указывается как полезные данные XML, и здесь добавляется в запрос в виде потока байтов.

byte[] contentInBytes = Encoding.UTF8.GetBytes(xml);
                        
using (Stream requestStream = request.GetRequestStream())
    requestStream.Write(contentInBytes, 0, contentInBytes.Length);

5. Прослушивание ответа от WNS, которое подтверждает получение уведомления

Примечание.

Вы никогда не получите подтверждение доставки для уведомления, просто подтверждение того, что оно было получено WNS.

using (HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse())
    return webResponse.StatusCode.ToString();

6. Обработка кодов ответов WNS

Существует множество кодов откликов, которые служба приложений может получать при отправке уведомления. Некоторые из этих кодов ответов более распространены, чем другие, и их можно легко решать в блоке catch.

catch (WebException webException)
{
    HttpStatusCode status = ((HttpWebResponse)webException.Response).StatusCode;

HttpStatusCode.Unauthorized: срок действия указанного маркера доступа истек. Получите новый, а затем повторите попытку отправки уведомления. Так как срок действия кэшированного маркера доступа истекает через 24 часа, вы можете ожидать получения ответа от WNS по крайней мере один раз в день. Рекомендуется реализовать максимальную политику повторных попыток.

    if (status == HttpStatusCode.Unauthorized)
    {
        GetAccessToken(secret, sid);
        return PostToWns(uri, xml, secret, sid, notificationType, contentType);
    }

HttpStatusCode.Gone / HttpStatusCode.NotFound: URI канала больше недействителен. Удалите этот канал из базы данных, чтобы предотвратить дальнейшие попытки отправки уведомления в него. При следующем запуске приложения запросите новый канал WNS. Приложение должно обнаружить, что его канал изменился, что должно активировать приложение для отправки нового URI канала на сервер приложений. Дополнительные сведения см. в статье "Как запрашивать, создавать и сохранять канал уведомлений".

    else if (status == HttpStatusCode.Gone || status == HttpStatusCode.NotFound)
    {
        return "";
    }

HttpStatusCode.NotAcceptable: этот канал регулируется WNS. Реализуйте стратегию повторных попыток, которая экспоненциально уменьшает объем отправленных уведомлений, чтобы предотвратить регулирование снова. Кроме того, переосмыслите сценарии, которые вызывают регулирование уведомлений. Вы предоставите более широкий пользовательский интерфейс, ограничив уведомления, которые вы отправляете тем, кто добавляет истинное значение.

    else if (status == HttpStatusCode.NotAcceptable)
    {
        return "";
    }

Другие коды ответов: WNS ответил с менее распространенным кодом ответа. Зайдите в журнал этого кода, чтобы помочь в отладке. Полный список кодов ответов WNS см . в заголовках запросов и ответов службы push-уведомлений.

    else
    {
        string[] debugOutput = {
                                   status.ToString(),
                                   webException.Response.Headers["X-WNS-Debug-Trace"],
                                   webException.Response.Headers["X-WNS-Error-Description"],
                                   webException.Response.Headers["X-WNS-Msg-ID"],
                                   webException.Response.Headers["X-WNS-Status"]
                               };
        return string.Join(" | ", debugOutput);            
    }

7. Инкапсулировать код в одну функцию

В следующем примере код, приведенный на предыдущих шагах, упаковается в одну функцию. Эта функция создает HTTP-запрос POST, содержащий уведомление, которое отправляется в WNS. Изменив значение параметра типа и изменив дополнительные заголовки, этот код можно использовать для всплывающих уведомлений, плитки, значка или необработанных push-уведомлений. Эту функцию можно использовать в составе кода облачного сервера.

Обратите внимание, что обработка ошибок в этой функции включает ситуацию, когда срок действия маркера доступа истек. В этом случае он вызывает другую облачную функцию сервера, которая повторно выполняет проверку подлинности с помощью WNS, чтобы получить новый маркер доступа. Затем он создает новый вызов исходной функции.

// Post to WNS
public string PostToWns(string secret, string sid, string uri, string xml, string notificationType, string contentType)
{
    try
    {
        // You should cache this access token.
        var accessToken = GetAccessToken(secret, sid);

        byte[] contentInBytes = Encoding.UTF8.GetBytes(xml);

        HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
        request.Method = "POST";
        request.Headers.Add("X-WNS-Type", notificationType);
        request.ContentType = contentType;
        request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken.AccessToken));

        using (Stream requestStream = request.GetRequestStream())
            requestStream.Write(contentInBytes, 0, contentInBytes.Length);

        using (HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse())
            return webResponse.StatusCode.ToString();
    }
    
    catch (WebException webException)
    {
        HttpStatusCode status = ((HttpWebResponse)webException.Response).StatusCode;

        if (status == HttpStatusCode.Unauthorized)
        {
            // The access token you presented has expired. Get a new one and then try sending
            // your notification again.
              
            // Because your cached access token expires after 24 hours, you can expect to get 
            // this response from WNS at least once a day.

            GetAccessToken(secret, sid);

            // We recommend that you implement a maximum retry policy.
            return PostToWns(uri, xml, secret, sid, notificationType, contentType);
        }
        else if (status == HttpStatusCode.Gone || status == HttpStatusCode.NotFound)
        {
            // The channel URI is no longer valid.

            // Remove this channel from your database to prevent further attempts
            // to send notifications to it.

            // The next time that this user launches your app, request a new WNS channel.
            // Your app should detect that its channel has changed, which should trigger
            // the app to send the new channel URI to your app server.

            return "";
        }
        else if (status == HttpStatusCode.NotAcceptable)
        {
            // This channel is being throttled by WNS.

            // Implement a retry strategy that exponentially reduces the amount of
            // notifications being sent in order to prevent being throttled again.

            // Also, consider the scenarios that are causing your notifications to be throttled. 
            // You will provide a richer user experience by limiting the notifications you send 
            // to those that add true value.

            return "";
        }
        else
        {
            // WNS responded with a less common error. Log this error to assist in debugging.

            // You can see a full list of WNS response codes here:
            // https://msdn.microsoft.com/library/windows/apps/hh868245.aspx#wnsresponsecodes

            string[] debugOutput = {
                                       status.ToString(),
                                       webException.Response.Headers["X-WNS-Debug-Trace"],
                                       webException.Response.Headers["X-WNS-Error-Description"],
                                       webException.Response.Headers["X-WNS-Msg-ID"],
                                       webException.Response.Headers["X-WNS-Status"]
                                   };
            return string.Join(" | ", debugOutput);            
        }
    }

    catch (Exception ex)
    {
        return "EXCEPTION: " + ex.Message;
    }
}

// Authorization
[DataContract]
public class OAuthToken
{
    [DataMember(Name = "access_token")]
    public string AccessToken { get; set; }
    [DataMember(Name = "token_type")]
    public string TokenType { get; set; }
}

private OAuthToken GetOAuthTokenFromJson(string jsonString)
{
    using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
    {
        var ser = new DataContractJsonSerializer(typeof(OAuthToken));
        var oAuthToken = (OAuthToken)ser.ReadObject(ms);
        return oAuthToken;
    }
}

protected OAuthToken GetAccessToken(string secret, string sid)
{
    var urlEncodedSecret = HttpUtility.UrlEncode(secret);
    var urlEncodedSid = HttpUtility.UrlEncode(sid);

    var body = String.Format("grant_type=client_credentials&client_id={0}&client_secret={1}&scope=notify.windows.com", 
                             urlEncodedSid, 
                             urlEncodedSecret);

    string response;
    using (var client = new WebClient())
    {
        client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
        response = client.UploadString("https://login.live.com/accesstoken.srf", body);
    }
    return GetOAuthTokenFromJson(response);
}

В следующем примере показан пример содержимого запроса HTTP POST для всплывающего push-уведомления.

POST https://db3.notify.windows.com/?token=AgUAAADCQmTg7OMlCg%2fK0K8rBPcBqHuy%2b1rTSNPMuIzF6BtvpRdT7DM4j%2fs%2bNNm8z5l1QKZMtyjByKW5uXqb9V7hIAeA3i8FoKR%2f49ZnGgyUkAhzix%2fuSuasL3jalk7562F4Bpw%3d HTTP/1.1
Authorization: Bearer EgAaAQMAAAAEgAAACoAAPzCGedIbQb9vRfPF2Lxy3K//QZB79mLTgK
X-WNS-RequestForStatus: true
X-WNS-Type: wns/toast
Content-Type: text/xml
Host: db3.notify.windows.com
Content-Length: 196

<toast launch="">
  <visual lang="en-US">
    <binding template="ToastImageAndText01">
      <image id="1" src="World" />
      <text id="1">Hello</text>
    </binding>
  </visual>
</toast>

Ниже показан пример HTTP-ответа, отправляемого на облачный сервер WNS в ответ на HTTP-запрос POST.

HTTP/1.1 200 OK
Content-Length: 0
X-WNS-DEVICECONNECTIONSTATUS: connected
X-WNS-STATUS: received
X-WNS-MSG-ID: 3CE38FF109E03A74
X-WNS-DEBUG-TRACE: DB3WNS4011534

Итоги

В этом кратком руководстве вы составили HTTP-запрос POST для отправки в WNS. WNS, в свою очередь, предоставляет уведомление приложению. К этому моменту вы зарегистрировали приложение, выполнили проверку подлинности облачного сервера с помощью WNS, создали XML-содержимое для определения уведомления и отправили это уведомление с сервера в приложение.