Archivi tag: W3C

Comprimere il contenuto di una risposta web, e analisi dell’Accept-Encoding

Il seguente articolo, è inerente c# e in particolare all’integrazione di un CompressionFilter nel  workflow di una chiamata web mediante il framework MVC2 di Microsoft, ma è applicabile in linea di massima a tutti i linguaggi. Si tratta fondamentalmente di applicare un algoritmo di compressione ai dati in output da una chiamata http, in modo da far viaggiare meno dati sulla rete, e accertarsi di quale metodi di compressione il client chiamante supporti.

Ci sono diversi tutorial sulla rete riguardo a questa problematica, ma è difficile trovare una risposta assoluta in quanto il vero problema non è la creazione di una funzione per la compressione (già implementate nei vari framework), ma nella decodifica corretta dell’Header Accept-Encoding che ci aiuta a dedurre che cosa supporti il chiamante, evitando così di applicare meccanismi di compressione non graditi o non supportati.

Banalmente basterebbe controllare se nell’header in questione sia presente la stringa gzip o deflate ,ad indicare che il client supporta i due algoritmi di compressione standard, ma in realtà le specifiche W3C sull’encoding, prevedono un funzionamento molto piu complesso.

 http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

Questo tipo di Headers si basano su un concetto di preferenza e pesi..

Alcuni esempi di valori possibili:

  • il più banale “gzip” oppure solo “deflate” ad indicare l’algoritmo desiderato.
  • “gzip,deflate” ad indicare la sequenza delle preferenze,
  • “gzip;q=0,deflate” ad indicare che gzip non dovrebbe minimamente essere usato (ha un peso Q pari a zero)
  • “gzip;q=0.4,deflate;q=0.4,identity;q=0.2” ad indicare la ripartizione tra le preferenze..
  • identity sta a indicare che non va assolutamente usato un content-encoding.. se espresso senza q, il default sui parametri e’ q=1 e quindi avrebbe sempre la precedenda come in questo esempio: “gzip;q=0.5,deflate;q=0.5,identity”..
  • “*” sta invece a indicare qualsiasi encoding

Da quello che si evince e’ impossibile andare a cercare semplicemente se nella stringa sia presente gzip o deflate.. perche si rischia di incappare in errori (come da esempio con lo “*” o con identity dove q=1)

Sulla rete si trovano gia delle List per valori pesati oppure realizziamo noi direttamente una semplice classe che tokenizza la stringa usando la virgola come separatore e poi tokenizza ogni token utlizzando il “;” . Ovviamente in caso di “*” la scelta di default possiamo attribuirla noi ad esempio gzip. Il sort dei token e’ eseguito sulla base del valore di q, andando a mettere q=1 a tutti gli elementi con q non specificato.

Poi passiamo alla parte MVC e realizziamo un ActionAttributeFilter che utilizzeremo per annotare le Action nel nostro Controller , che saranno sottoposte a compressione.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System;

using System.Collections.Generic;

using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Collections.Specialized;
using System.IO.Compression;
using System.IO;

public class CompressionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{

HttpRequestBase request = filterContext.HttpContext.Request;
string acceptEncodingHeader = request.Headers["Accept-Encoding"];
if (acceptEncodingHeader == null) return;

HttpResponseBase response = filterContext.HttpContext.Response;

MyFantasticQList encodingPreference = new MyFantasticQList(acceptEncodingHeader);

encodingPreference.setDefaultPreference("gzip");

string preferred = encodingPreference.findPreferred();

switch(preferred.Name){

case "gzip":

Response.AppendHeader("Content-Encoding""gzip");

Response.Filter = new GZipStream(Response.Filter, CompressionMode.Compress);

break;

case "deflate":

Response.AppendHeader("Content-Encoding""deflate");

Response.Filter = new DeflateStream(Response.Filter,CompressionMode.Compress);

break;

default:

break;

}

}

Realizzato il nostro CompressFilterAttribute, nel nostro controller basta annotare le action che devono restituire contenuto compresso:

 

1
2
3
4
5
6
7
8
9
[HttpPost()]

[CompressionFilter]

public ActionResult ListaElementi(string key){

//codice dell'action

}

In automatico prima della response, verrà applicato il content encoding corretto .

Ciao.

 

Ottenere le coordinate GPS dal browser con HTML5

Un modo semplice e veloce per ottenere le coordinate gps ,in una pagina html, mediante html5 .

La funziona javascript getCurrentPosition interroga ,se disponibile ,il nostro dispositivo gps (previa richiesta di autorizzazione)  . Se non riesce ad ottenerla da gps può comunque derivarla dall’indirizzo ip, ma il comportamento e le regole sono completamente a carico dell’implementazione del browser.

Questa è la funzione che va usata:

1
2
3
4
5
6
7
8
9
10
11
12
13
if(navigator.geolocation){

navigator.geolocation.getCurrentPosition(successCallback,

errorCallback,

{timeout:60000});

}else{

alert("Il browser non ha un dispositivo GPS");

}

Come vedete la funzione ha due callback, uno che viene invocato in caso di successo e l’altro di errore. Nei parametri e’ possibile impostare il timeout, cioè il tempo di attesa di synch con il gps prima di andare in errore di timeout. Da notare che questo tempo scatta da prima che appaia la popup di richiesta autorizzazione, quindi influisce molto nell’usabilità dell’interfaccia utente. 60 secondi in genere sono un valore sufficiente.

Ora descriviamo cosa deve fare la nostra pagina con i due callback:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//callback in caso di successo

function successCallback(position){

alert("Posizione:[lat="+position.coords.latitude+"][long="+position.coords.longitude+"]");

}

//callback in caso di errore

function errorCallback(error){

switch(error.code){

case error.PERMISSION_DENIED: alert('accesso negato');break;

case error.POSITION_UNAVAILABLE:alert('impossibile ottenere posizione');break;

case error.TIMEOUT:alert('timeout');break;

default: alert('errore generico');break;

}

}

Alcuni browser memorizzano l’autorizzazione con durata giornaliera, altri richiedono l’autorizzazione ogni volta.

Una cosa a cui prestare attenzione è il fatto che la funzione è asincrona, quindi difficile da gestire all’interno di un evento di button pressed o cose simili.

Consigli:

Meglio sfruttarlo nell’onload del document , magari facendo una pagina adHoc che aspetti il timeout della funzione mostrando l’icona di loadinprogress, in modo da far capire all’utente che la lettura dei dati dal gps è in corso.

Qui troverete una documentazione accurata, anche se solo in draft, ma vi consiglio di testare le vostre pagine con tutti i browser in quanto hanno comportamenti molto differenti.

http://dev.w3.org/geo/api/spec-source.html