Avvio rapido: Invio di una notifica push (XAML)

Il server cloud può inviare una notifica push all'app tramite Windows Push Notification Services (WNS). Questa procedura si applica a riquadri, avvisi popup, badge e notifiche push non elaborate.

Obiettivo: creare e inviare un riquadro, un avviso popup, un badge o una notifica push non elaborata.

Prerequisiti

Per comprendere questo argomento o per usare il codice fornito, è necessario:

Istruzioni

1. Includere i riferimenti allo spazio dei nomi necessari

Gli esempi forniti in questo argomento possono essere usati così come sono, ma richiedono che il codice includa questi riferimenti allo spazio dei nomi:

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

2. Crea una richiesta HTTP POST

Il uri parametro è l'URI (Uniform Resource Identifier) del canale richiesto dall'app e passato al server cloud. Per ulteriori informazioni, vedere Come richiedere, creare e salvare un canale di notifica.

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

3. Aggiungi le intestazioni obbligatorie

Esistono quattro intestazioni obbligatorie che devono essere incluse in tutte le notifiche push: X-WNS-Type, Content-Type, Content-Length e Authorization.

  • L'intestazione X-WNS-Type specifica se si tratta di un riquadro, un avviso popup, un badge o una notifica non elaborata.
  • Content-Type viene impostato a seconda del valore di X-WNS-Type.
  • Content-Length fornisce le dimensioni del payload di notifica incluso.
  • L'intestazione Authorization specifica le credenziali di autenticazione che consentono di inviare una notifica push all'utente tramite questo canale.

Il parametro accessToken dell'intestazione Authorization specifica il token di accesso, archiviato nel server, ricevuto da WNS quando il server cloud ha richiesto l'autenticazione. Senza il token di accesso, la notifica verrà rifiutata.

Per un elenco completo di possibili intestazioni, vedere Intestazioni delle richieste e delle risposte per il servizio di notifica push.

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

4. Aggiungi il contenuto preparato

Per quanto riguarda la richiesta HTTP, il contenuto XML della notifica è un BLOB di dati nel corpo della richiesta. Ad esempio, non viene eseguita alcuna verifica che il codice XML corrisponda alla specifica X-WNS-Type. Il contenuto viene specificato come payload XML e qui viene aggiunto alla richiesta come flusso di byte.

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

5. Ascolta una risposta da WNS che riconosce la ricezione della notifica

Nota

Non riceverai mai una conferma di recapito per una notifica, solo un riconoscimento che è stato ricevuto da WNS.

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

6. Gestisci i codici di risposta WNS

Esistono molti codici di risposta che il servizio app può ricevere quando invia una notifica. Alcuni di questi codici di risposta sono più comuni di altri e possono essere facilmente gestiti in un blocco catch.

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

HttpStatusCode.Unauthorized: il token di accesso presentato è scaduto. Ottienine uno nuovo e quindi riprova a inviare la notifica. Poiché il token di accesso memorizzato nella cache scade dopo 24 ore, puoi aspettarti di ottenere questa risposta da WNS almeno una volta al giorno. È consigliabile implementare un criterio massimo di ripetizione dei tentativi.

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

HttpStatusCode.Gone / HttpStatusCode.NotFound: l'URI del canale non è più valido. Rimuovi questo canale dal database per evitare ulteriori tentativi di invio di notifiche. Al successivo avvio dell'app da parte di questo utente, richiedere un nuovo canale WNS. L'app dovrebbe rilevare che il canale è stato modificato, che dovrebbe attivare l'app per inviare il nuovo URI del canale al server app. Per ulteriori informazioni, vedere Come richiedere, creare e salvare un canale di notifica.

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

HttpStatusCode.NotAcceptable: questo canale viene limitato da WNS. Implementa una strategia di ripetizione dei tentativi che riduce in modo esponenziale la quantità di notifiche inviate per evitare di essere nuovamente limitate. Inoltre, ripensare gli scenari che causano la limitazione delle notifiche. Si fornirà un'esperienza utente più completa limitando le notifiche inviate a quelle che aggiungono valore true.

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

Altri codici di risposta: WNS ha risposto con un codice di risposta meno comune. Registra questo codice per facilitare il debug. Per un elenco completo dei codici di risposta WNS, vedere Intestazioni di richiesta e risposta del servizio di notifica 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. Incapsula il codice in una singola funzione

L'esempio seguente crea un pacchetto del codice indicato nei passaggi precedenti in una singola funzione. Questa funzione compone la richiesta HTTP POST che contiene una notifica da inviare a WNS. Modificando il valore del parametro di tipo e modificando le intestazioni aggiuntive, questo codice può essere usato per notifiche push di tipo avviso popup, riquadro, badge o non elaborate. Puoi usare questa funzione come parte del codice del server cloud.

Si noti che la gestione degli errori in questa funzione include la situazione in cui il token di accesso è scaduto. In questo caso, chiama un'altra funzione del server cloud che esegue nuovamente l'autenticazione con WNS per ottenere un nuovo token di accesso. Esegue quindi una nuova chiamata alla funzione originale.

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

Di seguito viene illustrato il contenuto di esempio per una richiesta HTTP POST per una notifica push di tipo avviso popup.

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>

Di seguito viene illustrata una risposta HTTP di esempio, inviata al server cloud da WNS in risposta alla richiesta 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

Riepilogo

In questa guida introduttiva è stata composta una richiesta HTTP POST da inviare a WNS. WNS, a sua volta, recapita la notifica all'app. A questo punto, è stata registrata l'app, autenticato il server cloud con WNS, creato contenuto XML per definire la notifica e inviato tale notifica dal server all'app.