Progress di una chiamata Silverlight ad un servizio

di Cristian Civera, in Silverlight,

Ormai si sa, Silverlight come unico strumento di accesso ad informazioni remote, permette di usare i servizi, siano essi sviluppati lato server in asmx, WCF o altre tecnologie. Non è propriamente l'unica perché possiamo effettuare chiamate HTTP generiche e tramite REST/POX ottenere sottoforma di Json e XML i dati. Ad ogni modo i servizi usano uno standard e sono molto comodi e facili da utilizzare, grazie alla generazione automatica del proxy.

Durante un corso mi è stato però chiesto se possiamo conoscere lo stato di avanzamento di una chiamata ad un servizio. Partiamo dal presupposto che un servizio non dovrebbe mai restituire una quantità notevole di dati per due principali motivi:

  • è una chiamata HTTP, se qualcosa si interrompe siamo costretti a rieffettuare la chiamata e riottenere l'intera risposta;
  • è probabile che tutti questi dati non servano per intero all'utente fin dall'inizio e una buona politica di frammentazione di download delle informazioni è la strada ideale.

Ad ogni modo, un servizio che restituisce 1/5MB può essere accettabile e l'ideale sarebbe fornire lo stato di avanzamento all'utente. Per capire come conoscere lo stato di avanzamento dobbiamo prima di tutto sapere che sia Silverlight, lato client, che WCF, lato server, bufferizzano la richiesta e la risposta. Questo significa che entrambi preparano il messaggio SOAP, lo serializzano e lo mandano in risposta, o viceversa, leggono i byte della richiesta/risposta e solo quando interamente caricata preparano il messaggio e lo interpretano.

Poiché a noi interessa l'aspetto Silverlight dobbiamo prima di tutto modificare nel file ServiceReferences.ClientConfig l'attributo transferMode:

<basichttpbinding>
  <binding transfermode="StreamedResponse">
</binding>

In questo modo il binding HTTP di Silverlight comincerà a leggere la risposta pian piano che i byte giungono dal server. Questo però non basta perché non abbiamo cognizione dell'avanzamento della lettura della risposta. In Silverlight 4.0 possiamo scrivere behavior, binding e inserire message inspector, tutte cose ereditate da WCF del .NET Framework, ma non possiamo riscrivere parzialmente il basicHttpBinding e in generale HttpChannelFactory. Fortunatamente questo oggetto sfrutta al suo interno, per effettuare fisicamente la chiamata HTTP, la classe HttpWebRequest.

Per conoscere lo stato di avanzamento possiamo quindi personalizzare il modo in cui l'HttpWebRequest viene creato. Per farlo nel .NET Framework, ma anche in Silverlight, abbiamo a disposizione il metodo statico WebRequest.RegisterPrefix per indicare, in base allo schema, quale IWebRequestCreate utilizzare. Normalmente possiamo scegliere tra WebRequestCreator.BrowserHttp e WebRequestCreator.ClientHttp. Il primo sfrutta lo stesso motore del browser e contiene quindi i cookie del dominio e permette al browser di leggere la risposta, mentre il secondo scavalca il browser e permette di effettuare chiamate HTTP in liberà.

L'idea è quindi di creare un IWebRequestCreate e relativi HttpWebRequest, HttpWebResponse e Stream personalizzati che mi permettano di sapere a che punto si trova la lettura dello Stream. Quando infatti impostiamo il transferMode a streamed viene impostato sull'oggetto HttpWebRequest la proprietà AllowReadStreamBuffering a false, permettendo la lettura dello Stream mentre riceviamo i byte.

Per andare al sodo, qua trovate un'applicazione di esempio. Prima di tutto registro il mio creator personalizzato:

WebRequest.RegisterPrefix("http://", AdvancedWebRequestCreator.Default);

Tramite esso posso poi intercettare l'evento che mi notifica lo stato di avanzamento di una chiamata.

AdvancedWebRequestCreator.Default.UriProgressChanged += new EventHandler<UriProgressChangedEventArgs>(Default_UriProgressChanged);

Nell'evento so l'URI del servizio che sto chiamando, la percentuale, i byte scaricati e totali. Ho anche aggiunto una proprietà per indicare, per ogni URI, quale creator utilizzare, utile per quando usiamo i due client (http e browser) in modo misto.

AdvancedWebRequestCreator.Default.Mappings.Add(new UriCreateMapping(new Uri("http://sito.it"), WebRequestCreator.ClientHttp));

Per il resto basta chiamare il servizio come normalmente facciamo. Un'ultima nota per quanto riguarda WCF lato server: il transferMode dev'essere obbligatoriamente su buffered altrimenti la risposta non conterrà il ContentLength ed è impossibile per noi indicare lo stato.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese