Technology Experience
.NET World

La parte server dell’invio di una push notification con una tile ed un’immagine dinamica

Nei giorni scorsi mi sono imbattuto in un problema che mi ha fatto perdere tantissimo, ma davvero tantissimo tempo, problema che non ho visto risolto da nessuna parte. Spero con questo articolo di aiutare tutti coloro che in futuro potranno ricadere nello stesso scenario.

Sto parlando di inviare tramite push notification una nuova tile relativa ad un’app Windows 10, quindi UWP. Ma facciamo un passo indietro.

Il blocco XML più semplice che un server deve inviare al client per poter aggiornare una tile è:

<tile>
<visual version="2">
<binding template="TileWide310x150Image">
<image id="1" src="http://server/nomeimmagine.png"/>
</binding>
</visual>
</tile>

Ci sono tanti template da poter utilizzare, chiaramente. Questo è il template TileWide310x150Image, che invia al client le informazioni minime per aggiornare solo la tile rettangolare 310×150. Il parametro src indicato nell’XML è semplicemente l’url della nuova immagine. Nulla di particolarmente complicato. Invio un’immagine ogni volta diversa, e quindi la tile su ogni client si aggiorna.

Il problema arriva quando invece di puntare ad un’immagine fissa (jpg o png, per esempio), voglio puntare un qualche url che invece è una Action di ASP.NET MVC, Action che restituisce un’immagine sotto forma, ovviamente, di binario.

Immaginate di avere ad esempio un codice server simile al seguente:

public ActionResult RandomBackground()
{
MemoryStream ms = new MemoryStream();
Image image = new Bitmap(320, 240);

using (Graphics drawing = Graphics.FromImage(image))
{
Random rnd = new Random((int)DateTime.Now.Ticks);
int r = rnd.Next(0, 256);
int g = rnd.Next(0, 256);
int b = rnd.Next(0, 256);
Color c = Color.FromArgb(255, r, g, b);
drawing.Clear(c);

image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
}

return File(ms.ToArray(), "image/png");
}

Questa è una Action di ASP.NET MVC, quindi stiamo ragionando sulla parte server dell’invio di una push notification. Questa Action risponde ad un url simile al seguente…

http://server/controller/RandomBackground

e non fa altro che restituire ogni volta un’immagine 320×240 di colore diverso, ad ogni colpo di F5. Supponiamo quindi di inviare una push notification come questa:

<tile>
<visual version="2">
<binding template="TileWide310x150Image">
<image id="1" src="http://server/controller/RandomBackground"/>
</binding>
</visual>
</tile>

Sui dispositivi Windows 8 / 8.1 / 10 questa push notification ha un comportamento ben particolare. La prima volta la notifica arriva, il device la riceve ed aggiorna la tile. Fin qua nulla di strano. Dalla seconda volta in poi la tile non si aggiorna più. Perchè? Deduco, dopo infinite prove & test, perchè l’url della tile è sempre lo stesso, e quindi il device reputa che sia inutile aggiornarla, perchè interpreta che sia sempre la stessa tile. Peccato, per il ragionamento di cui sopra, che in realtà l’url punti ad un contenuto dinamico generato server-side, e quindi DEVE aggiornarla, anche se l’url è lo stesso.

Sono inoltre giunto alla conclusione che il meccanismo di ricezione di push notification sul client ignori tutti i parametri passati in querystring, così url come questi sono esattamente gli stessi:

http://server/controller/RandomBackground?index=534543
http://server/controller/RandomBackground?a=blablabla
http://server/controller/RandomBackground?guid=589353985u39835u49

Per risolvere, bisogna effettivamente puntare ad un url differente. La firma della Action insomma deve diventare qualcosa del genere:

public ActionResult RandomBackground(string id)
{
// codice
}

Il parametro id non ci serve, ma a questo punto noi possiamo invocare l’Action in questo modo:

http://server/controller/RandomBackground/34
http://server/controller/RandomBackground/aaaa
http://server/controller/RandomBackground/fkldglkdfm34
http://server/controller/RandomBackground/;ert;54353

Gli url qui puntano alla Action RandomBackground, passando diversi valori nel parametro id, valori che vengono bellamente ignorati, ma che semplicemente servono a scatenare effettivamente l’aggiornamento della tile. Cosa significa questo? Significa che server-side, in fase di preparazione dell’XML da inviare a ciascun client, l’url deve essere generato ogni volta diverso, quindi giocare con System.Random per fare una cosa del genere:

string tileUri = string.Format("http://server/controller/RandomBackground/{1}",
new Random((int)DateTime.Now.Ticks).Next(1, 999999));

L’url della tile, insomma, viene generato random ogni volta, mandato al client, che lo aggiorna senza farsi troppe domande. Ok, primo step risolto.

Ulteriori complicazioni

Fin qua, volendo, è tutto semplice, perchè la Action RandomBackground che abbiamo ipotizzato negli esempi qui sopra non ha bisogno di veri parametri di input. Supponiamo che la generazione della tile abbia a che fare con il meteo, e che la Action debba restituire l’immagine di una tile contenente le previsioni della provincia passata in input fra i parametri. Ad esempio:

http://server/controller/Meteo/Lodi
http://server/controller/Meteo/Roma
http://server/controller/Meteo/Lecce
http://server/controller/Meteo/Ancona

Ma torniamo ai discorsi di prima. Se inviassi questo url ad un device Windows 10, la prima volta la tile verrebbe aggiornata, dalla seconda volta in poi no. E quindi dobbiamo generare url ogni volta sempre diversi, accodando un secondo parametro fittizio come qui sotto:

http://server/controller/Meteo/Lodi/5437534
http://server/controller/Meteo/Roma/989
http://server/controller/Meteo/Lecce/1123
http://server/controller/Meteo/Ancona/9043

Ovviamente i numeri sono sparati a caso. Per ottenere questo, bisogna modificare le regole di routing di ASP.NET MVC, per indicare che nell’url possiamo indicare un parametro aggiuntivo opzionale. Quindi quello che ho fatto è stato andare nel file RouteConfig.cs e scrivere quanto segue:

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}/{key}",
defaults: new { controller = "Home", action = "Index",
id = UrlParameter.Optional,
key = UrlParameter.Optional }
);
}

Cioè: tutti gli url possono contenere un parametro {key} opzionale. Di conseguenza, la firma della nostra Action Meteo diventa:

public ActionResult Meteo(string id, string key)
{
// Codice
}

All’interno del parametro id avremmo la provincia passata. All’interno del parametro key arriverebbe un secondo parametro, generato ogni volta casualmente, bellamente ignorato, inserito al solo scopo di avere un url ogni volta differente.

Conclusioni

Direi che è tutto. Bel problema, non c’è che dire.

Finalmente risolto, e le mie tile arrivano puntuali e precise.

Send to Kindle

Igor Damiani

La sua passione per l'informatica nasce nella prima metà degli anni '80, quando suo padre acquistò un Texas Instruments TI-99. Da allora ha continuato a seguire l'evoluzione sia hardware che software avvenuta nel corso degli anni. E' un utente, un videogiocatore ed uno sviluppatore software a tempo pieno. Igor ha lavorato e lavora anche oggi con le più moderne tecnologie Microsoft per lo sviluppo di applicazioni: .NET Framework, XAML, Universal Windows Platform, su diverse piattaforme, tra cui spiccano Windows 10 piattaforme mobile. Numerose sono le app che Igor ha creato e pubblicato sul marketplace sotto il nome VivendoByte, suo personale marchio di fabbrica. Adora mantenere i contatti attraverso Twitter e soprattutto attraverso gli eventi delle community .NET.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.