Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Windows Push Notification Service, inviare notifiche all'utente

Link copiato negli appunti

Il Windows Push Notification Service (WNS) è un'infrastruttura cloud di Microsoft che permette di inviare toast e aggiornare tile e badge dal proprio servizio cloud verso un'applicazione Windows Store, gestendo i meccanismi della comunicazione con il dispositivo device in modo trasparente ed efficiente.

Richiedere e creare un notification channel

Il primo passo necessario per poter sfruttare il WNS per inviare notifiche alla nostra applicazione Windows Store consiste nel richiedere, tramite le API di WinRT, la creazione di un notification channel alla Notification Client Platform di Windows 8, la quale provvede a girare la richiesta al WNS.

Il WNS restituisce all'applicazione, sempre per il tramite la Notification Client Platform, una URI che rappresenta in modo univoco l'applicazione stessa (sul punto vedi anche più avanti), ed è a quest'ultima che spetta il compito di inviare l'URI così ricevuta al proprio servizio di back end, in modo che venga salvata in uno storage persistente (indipendente dal WNS).

A questo punto, ogni volta che il servizio di back end dovrà notificare qualcosa all'utente, dovrà semplicemente effettuare una richiesta HTTP POST su SSL (Secure Sockets Layer) al WNS usando l'URI che identifica quella particolare applicazione client.

Il WNS provvederà quindi a girare la notifica al device e, tramite sempre le librerie della Notification Client Platform di Windows 8, potrà essere utilizzata dall'applicazione per notificare un toast, per aggiornare il tile e/o il badge applicativo o inviare una raw notification (ossia un breve messaggio il cui payload è definito a livello applicativo).

Ad esempio, un'applicazione meteo potrebbe richiedere un notification channel al WNS e inviare al servizio in back end la URI ricevuta, assieme ad alcune informazioni aggiuntive come ad esempio l'elenco delle località preferite dell'utente. Questi dati possono essere quindi processati dalla logica di back end, ad esempio per avvertire l'utente tramite toast che sta per piovere o che la temperatura è scesa sotto una certa soglia, oppure semplicemente per aggiornare il tile applicativo con la temperatura attuale.

La prossima immagine sintetizza il flusso complessivo.



(fonte MSDN)

È importante sottolineare che un notification channel rappresenta un singolo utente su un singolo device per una specifica applicazione. Questo significa che due applicazioni sullo stesso device non riceveranno mai la stessa URI, così come la stessa applicazione su due device diversi riceverà due differenti canali. Il servizio di back end deve saper gestire queste eventualità (ad esempio serializzando più URI per lo stesso utente che ha installato la stessa applicazione su più device).

Per poter lavorare con le API di push notification, inserite nel namespace Windows.Networking.PushNotification, non è necessario aggiungere alcuna reference al progetto Windows Store.

La prima cosa da fare, come si è visto, è richiedere al WNS la creazione di un notification channel. Per far questo, è sufficiente utilizzare la funzione asincrona CreatePushNotificationChannelForApplicationAsync esposto dall'oggetto di tipo PushNotificationChannelManager. Questo metodo provvede a creare un notification channel e a restituire un oggetto di tipo PushNotificationChannel, la cui proprietà Uri espone l'identificativo univoco del canale. Il prossimo snippet ne mostra un esempio.

Occorre sempre ricordare di gestire l'eventuale fallimento dell'operazione, perché se la connessione a Internet non è disponibile al momento della chiamata della funzione CreatePushNotificationChannelForApplicationAsync, il sistema solleverà un'eccezione:

app.onloaded = function () {
	btnCreateChannel.addEventListener("click", createChannel_click);
}
function createChannel_click(args) {
	var pushNotifications = Windows.Networking.PushNotifications;
	var channelOperation = pushNotifications.PushNotificationChannelManager
		.createPushNotificationChannelForApplicationAsync()
			.then(function (reqChannel) {
				message.innerText = "Notification channel: " + reqChannel.uri;
			},
			function (error) {
				// Impossibile recuperare il notification channel
			});
}

Per testare questo semplicissimo codice d'esempio, puoi usare la seguente definizione HTML come riferimento per la pagina di default della tua app.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Demo.Html.it.WNS.JS</title>
<!-- WinJS references -->
<link href="//Microsoft.WinJS.2.0/css/ui-dark.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.2.0/js/base.js"></script>
<script src="//Microsoft.WinJS.2.0/js/ui.js"></script>
<!-- Demo.Html.it.WNS.JS references -->
<link href="/css/default.css" rel="stylesheet" />
<script src="/js/default.js"></script>
</head>
<body>
<button id="btnCreateChannel" class="button">Crea un canale</button>
<div id="message"></div>
</body>
</html>

La prossima immagine mostra l'URI ricevuta dal WNS:

A questo punto l'applicazione ha bisogno di inviare l'URI al back end applicativo in modo sicuro. Ad esempio, se l'URI deve essere comunicata a un servizio web, sarebbe meglio usare un algoritmo di cifratura del messaggio e usare HTTPS come protocollo di trasporto.

È importante sottolineare che l'applicazione dovrebbe richiedere un canale al WNS a ogni nuovo lancio. Non c'è infatti alcuna garanzia che l'URI rimanga la stessa ad ogni successiva richiesta (secondo la documentazione ufficiale MSDN, attualmente un notification channel rimane attivo per 30 giorni). Quanto al servizio di back end, la soluzione migliore sarebbe quella di controllare lato client se l'URI è cambiata e, in caso affermativo, aggiornare il servizio di back end con la nuova URI. Ma per poter effettuare questo controllo, occorre salvare l'URI corrente nello storage locale, in modo da poterla confrontare con la nuova URI ricevuta al successivo lancio dall'applicazione. In particolare, a ogni lancio l'applicazione deve:

  1. Richiedere un canale al WNS tramite il metodo CreatePushNotificationChannelForApplicationAsync.
  2. Controllare nello storage locale se c'è già un'URI salvata in precedenza.
  3. In caso positivo, confrontare la vecchia URI con la nuova e, se diverse, inviare la nuova URI al servizio di back-end e contemporaneamente persisterla nello storage locale.

Vale la pena precisare che un'app può avere più canali validi allo stesso tempo senza nessun problema. Un canale rimane attivo sino a quando non arriva a scadenza: fino a quel momento, l'applicazione potrà continuare a usare il canale.

Qualora l'app non abbia più necessità di usare il canale, è possibile invocare il metodo Close dell'oggetto PushNotification per chiudere esplicitamente il canale. Qualunque notifica inviata successivamente su questo canale non verrà "recapitata". Se il canale è usato per aggiornare il tile applicativo, è buona norma, dopo aver chiuso il canale, ripristinare il tile allo stato iniziale tramite il metodo Clear della classe TileUpdater.

Inviare una notifica al client dal servizio in back end

Ricevuto l'URI che identifica il notification channel, il back end ha il compito di salvare questa informazione, per poi usarla per inviare notifiche all'utente. Ad esempio, il servizio potrebbe salvare l'URI del canale (magari assieme alle preferenze dell'utente) in un database SQL Azure, o in una tabella del Windows Azure Storage Account. La logica di business del back end potrebbe poi utilizzare le preferenze dell'utente per decidere se e quando avvisare l'utente, inviando le notifiche al WNS tramite l'URI salvata in precedenza.

In particolare, il back end può inviare all'utente aggiornamenti da visualizzare tramite tile, badge o toast, semplicemente utilizzando la sintassi XML appropriata. Questa sintassi è identica a quella usata dall'applicazione client per compiere le medesime operazioni. Ad esempio, per inviare un toast testuale il payload XML sarà simile al seguente (lo stesso vale per tile e badge):

<toast launch="">
	<visual lang="it-IT">
		<binding template="ToastText01">
			<text id="1">Ricordati di prendere l'ombrello, sta per piovere!</text>
		</binding>
	</visual>
</toast>

Il frammento XML deve essere inviato tramite HTTPS al WNS assieme al token di autenticazione (authentication token) fornito dal servizio Windows Live. Per ottenere il token, è possibile utilizzare la classe HttpClient per effettuare un roundtrip verso il servizio di autenticazione.

Il prossimo snippet mostra un esempio di richiesta (nell'esempio proposto, il back end è in C#, ma ovvimanente il servizio può essere sviluppato in qualunque linguaggio e su qualunque piattaforma server-side):

private OAuthToken GetOAuthToken()
{
	String secret = "la_secret_key_ottenuta_dal_Windows_Store";
	String sid = "sid_ottenuto_dal_Windows_Store";
	var encSecret = HttpUtility.UrlEncode(secret);
	var encSid = HttpUtility.UrlEncode(sid);
	var body = String.Format("grant_type=client_credentials&client_id={0}&client_secret={1}&scope=notify.windows.com", encSid, encSecret);
	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);
	}
}

Questo snippet mostra come costruire la richiesta da inviare al servizio Windows Live: in primo luogo, l'URL del servizio di autenticazione (https://login.live.com/accesstoken.srf), il content-type del messaggio ("application/x-www-form-urlencoded"), nonché una serie di parametri obbligatori che devono essere contenuti nel body del messaggio:

Prametro Descrizione
grant_type deve essere impostato su "client_credentials".
client_id identifica il Package security identifier (SID) assegnato al servizio cloud al momento della registrazione dell'app sul Windows Store.
client_secret la secret key per il servizio cloud assegnata al momento della registrazione dell'app sul Windows Store.
scope deve essere impostato su "notify.windows.com".

Per autenticare la richiesta al Windows Live Service, è dunque necessario inviare il SID e la secret key ricevuti durante il processo di registrazione al servizio. Nel prossimo paragrafo vedremo come recuperare questi dati. Per il momento, è importante precisare che, proprio perché confidenziali, il back end dovrebbe conservare queste due informazioni in un posto sicuro.

La risposta del Windows Live Services utilizza il formato JavaScript Object Notation (JSON) e contiene direttamente il token di autorizzazione. Il seguente snippet ne mostra un esempio:

HTTP/1.1 200 OK
Cache-Control: no-store
Content-Length: 422
Content-Type: application/json
{
"access_token":"EgAcAQMAAAAALYAAY/c+Huwi3Fv4Ck10UrKNmtxRO6Njk2MgA=",
"token_type":"bearer"
}

Il primo parametro, access_token, rappresenta, come il nome suggerisce, il token che il servizio cloud dovrà utilizzare per inviare le notifiche. Il secondo parametro, token_type, presenta un valore costante, ossia bearer.

La risposta può quindi essere deserializzata aggiungendo le seguenti linee di codice:

private OAuthToken GetOAuthToken()
{
	// ...
	// parte da aggiungere
	using (var ms = newMemoryStream(Encoding.Unicode.GetBytes(response)))
	{
		var ser = newDataContractJsonSerializer(typeof(OAuthToken));
		var oAuthToken = (OAuthToken)ser.ReadObject(ms);
		return oAuthToken;
	}
}

Il codice utilizza la classe MemoryStream per incapsulare la stringa JSON contenuta nella risposta, quindi istanzia un oggetto di tipo DataContractJsonSerializer per leggere lo stream e trasformarlo in un token di tipo OAuth. La classe OAuthToken è definita come segue:

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

La classe OAuthToken espone una proprietà denominata AccessToken che rappresenta l'header di autenticazione che il back end dovrà utilizzare assieme alla richiesta di notifica.

In particolare, la richiesta da parte del servizio di back end deve includere i seguenti header obbligatori:

Header Descrizione
Authorization si tratta del classico header HTTP standard. Nel caso del servizio cloud, questo header contiene il token di autorizzazione.
Content-Type anche questo è il tradizionale header HTTP standard. Nel caso di toast, tile e badge, questo header deve essere impostato a "text/xml", mentre nel caso di raw notification, il valore dev'essere "application/octet-stream".
Content-Length header HTTP standard che indica le dimensioni del payload.
X-WNS-Type header custom che definisce il tipo di payload: tile, toast, badge o raw.

Oltre agli header obbligatori, la richiesta di notifica può includere uno dei seguenti header (opzionali):

Header Descrizione
X-WNS-Cache-Policy abilita il caching della notifica. Non si applica ai toast.
X-WNS-RequestForStatus richiede che nella risposta siano indicati lo status del device e quello della connessione con il WNS.
X-WNS-Tag il tag da associare a quella particolare notifica, per i tile che supportano il meccanismo di notification queue (vedi la relativa documentazione su MSDN).
X-WNS-TTL un intero che specifica, in secondi, il "time to live" (TTL).

Qui di seguito è mostrato un esempio di possibile richiesta dal servizio di back end al WNS contenente tutti gli elementi necessari:

POST https://db3.notify.windows.com/?token=AfUAABBCQmGg7OMlCg%2fK0K8rBPcBqHuy%2b1rTSNPMuIzF6BtvpRdT7DM4j%2fs%2bNNm8z5l1QKZMtyjByKW5uXqb9V7hIAeA3i8FoKR%2f49ZnGgyUkAhzix%2fuSuasL3jalk7562F4Bpw%3d HTTP/1.1
Authorization: Bearer agFaAQDAAAAEgAAACoAAPzCGedIbQb9vRfPF2Lxy3K//QZB78mLTgK
X-WNS-RequestForStatus: true
X-WNS-Type: wns/toast
ContentType: text/xml
Host: db3.notify.windows.com
Content-Length: 189
<toast launch="">
<visual lang="it-IT">
<binding template="ToastText01">
<text id="1">Ricordati di prendere l'ombrello, sta per piovere!</text>
</binding>
</visual>

Una volta ottenuto il token di autorizzazione e preparato l'XML per il payload (in questo caso, un toast), possiamo inviare al WNS la richiesta di notifica:

private void PushNotification()
{
	OAuthToken token = this.GetOAuthToken();
	String uri = "Uri_ricevuta_dal_WNS";
	var toastInBytes = File.ReadAllBytes(@"toast.xml");
	HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
	request.Method = "POST";
	request.Headers.Add("X-WNS-Type", "wns / toast");
	request.ContentType = "text/xml";
	request.Headers.Add("Authorization", String.Format("Bearer {0}", token.AccessToken));
	using (Stream requestStream = request.GetRequestStream())
		requestStream.Write(toastInBytes, 0, toastInBytes.Length);
	using (HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse())
		Console.WriteLine(webResponse.StatusCode.ToString());
}

Una volta ottenuto il token di autorizzazione, il codice lo utilizza per creare l'authentication header per la richiesta al WNS. Quindi, dopo aver costruito il resto del messaggio, il codice effettua una POST verso il servizio, passando come payload il frammento di XML che definisce il toast.

Se la richiesta ha avuto successo, il servizio otterrà come risposta l'HTTP status code 200 OK, assieme al token di autorizzazione. Se invece l'autenticazione fallisce, riceverà un 400 Bad Request (gli altri codici di errore sono descritti qui, assieme alla descrizione delle possibili cause).

Per evitare roundtrip, puoi tenere in cache il token di autorizzazione ricevuto, ma in questo caso accertati di intercettare l'eccezione derivante da un token scaduto. Il prossimo snippet mostra come gestire questa eventualità:

catch (WebException webException)
{
	string exceptionDetails = webException.Response.Headers["WWW-Authenticate"];
	if (exceptionDetails.Contains("Token expired"))
	{
		var token = GetOAuthToken(secret, sid);
		// Impostare politica di retry
	}
}

Registrare la propria app presso il WNS

Per poter ottenere il SID e la secret key, è necessario registrare la propria applicazione con il servizio tramite la dashboard del Windows Store, come illustrato nella prossima immagine, presa dalla documentazione ufficiale su MSDN (alla quale si rinvia per i dettagli della procedura di registrazione):



(fonte MSDN)

Tieni presente che per poter registrare l'app al WNS occorre aver precedentemente riservato sul Windows Store il nome dell'applicazione (non è invece necessario caricare i pacchetti applicativi).

Nella pagina Push notification and Live Connect services info, per visualizzare il SID e la chiave segreta è sufficiente cliccare sul link Authenticating Your Service, come mostrato nella prossima immagine:



(fonte MSDN)

Intercettare le notifiche

Quando l'applicazione è in esecuzione e una notifica è inviata al device, è possibile per l'applicazione intercettare e gestire il messaggio prima che il toast sia mostrato, o il tile e il badge aggiornati. In questo modo, l'app è in grado di modificare o eventualmente sopprimere la notifica. Riprendendo l'esempio dell'applicazione meteo, se l'app riceve un toast con l'ultima temperatura rilevata nella località preferita dall'utente, ma questi già visualizzando relativa pagina sull'app, il messaggio potrebbe risultare fastidioso e superfluo. Per questo, la logica applicativa potrebbe decidere di intercettare e semplicemente sopprimere il toast sfruttando l'evento PushNotificationReceived.

Il prossimo snippet mostra un esempio di utilizzo di questo evento:

function createChannel_click(args) {
	var pushNotifications = Windows.Networking.PushNotifications;
	var channelOperation = pushNotifications.PushNotificationChannelManager.createPushNotificationChannelForApplicationAsync()
	.then(function (reqChannel) {
		// riga da aggiungere
		reqChannel.addEventListener("pushnotificationreceived", onNotificationReceived, false);
		message.InnerText = reqChannel.uri;
	},
	function (error) {
		// Impossibile recuperare il notification channel
	});
}

Il corrispondente event handler ispeziona la proprietà NotificationType, un enum che indica il tipo di notifica ricevuta (badge, tile, toast, raw) e, sulla base della propria logica applicativa, può eventualmente manipolarne il contenuto, come mostrato nel prossimo snippet.

function onNotificationReceived(e) {
	var notification;
	switch (e.notificationType) {
		case pushNotifications.PushNotificationType.toast:
			notification = e.toastNotification.content.getXml();
		break;
		case pushNotifications.PushNotificationType.tile:
			notification = e.tileNotification.content.getXml();
		break;
		case pushNotifications.PushNotificationType.badge:
			notification = e.badgeNotification.content.getXml();
		break;
		case pushNotifications.PushNotificationType.raw:
			notification = e.rawNotification.content;
		break;
	}
}

Dopo aver ispezionato il tipo di notifica ricevuto tramite la proprietà NotificationType dell'oggetto PushNotificationEventArgs ricevuto come parametro dall'event hanlder, il codice recupera il frammento XML corrispondente e, sulla base della propria logica interna, decide se e come utilizzare la notifica.

Finora abbiamo visto come intercettare le notifiche quando l'app è in esecuzione. Tuttavia è anche possibile creare e registrare un background task per eseguire codice in background quando l'app non è in foreground. Questo codice verrà eseguito in risposta ad una raw notification (per quanto riguarda la creazione e la registrazione di un background task si rinvia agli articoli di questa guida espressamente dedicati a questo argomento). Il seguente estratto mostra la sezione dell'application manifest contenente la definzione del background task: come si può notare, il task è di tipo pushNotification.

<Extensions>
	<Extension Category="windows.backgroundTasks">
		<BackgroundTasks>
			<Task Type="pushNotification" />
		</BackgroundTasks>
	</Extension>
</Extensions>

Quindi l'applicazione registra il task tramite un PushNotificationTrigger, il quale determina l'esecuzione del task all'arrivo di una nuova raw notification da parte del WNS. Il seguente estratto ne mostra un esempio (per l'esempio completo si rinvia all'articolo dedicato alla creazione di un background task [[LINK]]):

var builder = new Windows.ApplicationModel.Background.BackgroundTaskBuilder();
var trigger = new Windows.ApplicationModel.Background.PushNotificationTrigger();
builder.name = "sampleBackgroundTask";
builder.taskEntryPoint = "js\\sampleTask.js";
builder.setTrigger(trigger);

All'interno del background task è possibile recuperare, tramite la proprietà TriggerDetails, l'istanza della notifica ricevuta, nonché accedere al relativo contenuto tramite la proprietà Content, come mostrato nel seguente esempio:

var bgTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;
function doSomeWork() {
    var notificationContent = bgTaskInstance.triggerDetails.content;
    // analizza il contenuto della stringa
    close();
}
doSomeWork();

Come abbiamo visto in queste pagine, il Windows Push Notification Service di Microsoft semplifica notevolmente il lavoro dello sviluppatore, consentendo l'invio di toast, badge, tile e raw notification senza doversi preoccupare dei dettagli relativi alla comunicazione tra il servizio di backend, che racchiude la logica che presiede all'invio di messaggi all'utente, e l'applicazione Windows Store che riceve le comunicazioni provenienti dal cloud.

Ti consigliamo anche