Jquery & Web Services in .NET: Appunti tecnici

Tralasciando ovviamente cos'è Ajax, Jquery, Json e un web Services partiamo in tromba con una serie di consigli/appunti per chi vuole cominciare ad utilizzare jQuery per fare chiamate ajax ad una pagina o ad un servizio web:

 

  • javascript NON possiede l'intellisense e non c'è un check a compile-time. Questo vuol dire che se sbagli anche un solo carattere l'applicazione NON funzionerà. Ancora peggio, sarà difficile capire COSA non va.
    CONSIGLIO: usate la tecnica del copia & incolla: dal nome del file .ASMX al nome del metodo al nome del parametro! Risparmiere ore di insonnia..
  • Se usate una pagina WebForm come 'servizio' da interrogare allora ASSICURATEVI (ma con coscienza) di aver disabilitato a livello di direttiva di pagina: ValidateRequest="false"
    Nota: ho perso tipo 4gg di vita per capire che il campo di tinyMCE che spedivo via jQuery ad una pagina aspx veniva amabilmente sopresso senza alcuna apparente ragione..e si che nell'header http di request lo trovavo con Firebug!
  • Procuratevi il file jSon2.js dal sito JSON: contiene un ottimo javascript per la serializzazione e de-serializzazione
  • Attenti al cache! jQuery ha una sua cache e può essere gestita attraveso l'opzione cache: true/false. Ma anche la webform non scherza: se volete assicurarvi per qualsiasi chiamata un dato 'fresco fresco' inserite nell'evento pre_init: Response.Cache.SetNoStore();Response.Cache.SetCacheability(HttpCacheability.NoCache);
  • ATTENZIONE al charset: jQuery funziona di base con UTF-8 ergo spedirà i dati codificati in UTF-8.
  • il type deve essere sempre 'POST' quando si interroga un web service
  • Il datatype consigliato è sempre jSon (xml oramai lasciamolo per altre cose grazie :D): in questo caso alla chiamata del success, jQuery passa automaticamente il risultato della chiamata, parsizzato in un oggetto jquery: che comodità!
  • l'url deve essere sempre il percorso del file asmx/nome del metodo da richiamare
  • data DEVE essere SEMPRE passato come una stringa che contiene un json valido: in questo punto viene MOLTO comodo il file jSon2.js descritto prima con il suo metodo JSON.stringfy(oggettoJson) in quanto si occupa di eseguire il parsing corretto di un oggetto javascript contente una struttura jSon.
  • Async: se true verrà eseguita una chiamata ASINCRONA: vuol dire che il flusso del codice continuerà e la chiamata ajax verrà effettuata in background lasciando così l'utente a continuare a interargire con l'ambiente. Questo è molto 'cool' ma anche molto pericoloso. Trattare quindi questa feature con il dovuto rispetto.
    Consiglio: SE usate async: true ALLORA ricordatevi di definire SEMPRE un metodo per success!
    Se poi 'involucrate' la chiamata ajax in una funzione e questa non è l'ULTIMA operazione che viene eseguita _allora_ diventa obbligatorio mettere async = false! Altrimenti il codice che segue sarà molto probabilmente anacronistico (cool: sono riuscito a inserire sta parola in un post! +1.000pt) ..dal punto di vista dell'esecuzione prevista.
  • I parametri da passare ad un servizio web DEVONO avere lo stesso nome dei parametri della firma del metodo! Un parametro può anche NON essere passato: in quel caso si farrà assunto che il valore contenuto è il default (null o default: un int? avrà null e un Int32 avrà un 0)
  • Limiti di invio di json: vedi sotto il pezzo di web.config da aggiungere (raro ma può succedere di avere problemi)
  • Il web service da richiamare deve essere sempre risiedere nello stesso dominio! (se no bisogna creare un bridge con un webservice locale che faccia da tramite...generalmente è la soluzione da preferire)
  • Un web service deve sempre avere il seguente attributo attivo nella sua definizione:
    [System.Web.Script.Services.ScriptService]
    public class ...nomeservizio..

  • Un web service deve avere i seguenti attributi nel nome del metodo:
        [WebMethod(true)]
        [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
        public String NomeMetodo(parametri)..

  • Nel caso in cui vogliate usare una pagina aspx come datasource per una chiamata ajax usate l'ottima librereria Newtonsoft.Json che travete qui con una funzione del genere (da richiamare alla fine di tutto, passandogli come parametro il risultato della vostra interrogazione)
    protected void JsonResponse(object result)
        {
            Response.Clear();
            string res = JsonConvert.SerializeObject(result, Formatting.Indented);
            Response.ContentType = "application/json";
            Response.ContentEncoding = Encoding.UTF8;
            Response.Write(res);
        }

  • Occhio a Newtonsoft.Json: molti non hanno avuto problemi ma ultimamente leggo nei 'tweet' di Ayende alcuni problemi di prestazioni e non solo (vedi questo unit test)
  • Per passare come parametro una collection di elementi la cosa più semplice da fare è definirla come 'object' nella firma del metodo del webservice: es: gare.asmx/SalvaPunteggi(int idGara, object punteggi) dove punteggi contiene una collection di elementi punteggio (vedi esempio sotto)
  • Non tentate di accedere direttamente ad una pagina aspx (o quant'altro) che restituisce nello stream out un mime type di tipo 'application/json': è convenzione per i browser NON renderizzarne l'output...quindi se volete testare se la vostra pagina aspx restituisce realmente un json valido...fatelo in altra maniera ^_^

 

Il mistero di response.d e di eval('(':

Per chi non è scafatissimo sull'argomento, avrà notato che alle volte ci si imbatte in del codice come response.d piuttosto che un strano uso di eval come: eval('(' + jsonString + ')').

Uno dei grandi vantaggi di usare JSon è quello di poter parsizzare facilmente il contenuto. In pratica per trasformare una stringa che contiene una struttura json (es: "{numeriGiocati: [1,2,4]}" in un oggetto javascript contenete un jSon data, basta semplicemente usare eval.

Il fatto di usare la sintassi eval come descritto sopra, forza lo stesso eval a considerare il parametro passato (jsonString in questo caso) una expression e quindi il blocco "{}" che contiene la nostra struttura dati jSon NON verrà interpretata come un blocco di codice da eseguire!

Veniamo al mistero della proprietà .d. Innanzitutto è di 'proprietà' di Ajax.NET: ovvero quando utilizzate Ajax.NET (web service, web method) e eseguite un ritorno di data in formato JSon, quest'ultimo verrà inserito nella proprietà 'd'.
Quindi questo comportamento non accade normalmente nel 'web'. Quando poi consumate dei dati via Ajax.NET, questo problema non ce l'avete perchè il tutto viene trattato in maniera trasparente dal fw stesso.
.d nasce per una questione di sicurezza per impedire attacchi cross-site: se la struttura viene inserita nel grafo di d, allora non può essere eseguito del codice malevolo automaticamente. Ricordo che Eval permette l'interpretazione di blocchi di codice contenuti nella variabile stringa passata. Se questa non è formattata correttamente, viene eseguita invece che 'parsizzata'.

In pratica ajax.NET restituisce qualcosa come: {"d": "nome", "gigi"}
Questo risultato non può essere immediatamente parsizzato in jSon e quindi deve essere effettuata una ESPLICITA operazione di conversione sulla proprietà d.
Fw come jQuery non sono predisposti per questo comportamento e da qui nasce l'esigenza di verificare l'esistenza di d e di conseguenza parsizzarla correttamente.
Ecco come verificare e parsizzare correttamente la proprietà d:

var data = (typeof response.d) == 'string' ? eval('(' + response.d + ')') : response.d;

In pratica si verifica che .d contenga una stringa. Se si allora eseguo eval per farmi ritornare un oggetto javascript, altrimenti restituisco direttamente il contenuto di d.

 

Struttura di una chiamata Ajax di jQuery:

174f59ba-0ed9-4c05-a9c5-994461b426d7

Un esempio di "passingArguments" ovvero un oggetto jSon da passare al metodo:

4a0338e0-47f9-4d08-ac92-b2127ab0d43e

Come passare un array di elementi:

fbe356b2-d9dc-43f2-ad36-24a20cb93e30

strPass infine verrà passato come valore per la proprietà 'data' delle options di $.Ajax

Per la parte di servizio:

f679ce51-b74c-4417-baf5-5617bef71646

Come potete notare grazie a JavaScriptSerializer (Member of System.Web.Script.Serialization.JavaScriptSerializer) converto items (Object) in una lista di GiocatoreGaraDto.

 

Come consumare un array di elementi:

$.ajax..

ed06c266-b34d-4190-b917-28623646696e
dove AddAnno è una funzione che gestice un item per volta.

per la parte di servizio:

bf555be8-403f-4746-bac9-140472fef6a6

..e per fortuna che quando ho cominciato questo post mi son chiesto se non avrei avuto troppo poco da dire a riguardo..

 
 
  1. 11/05/2010 17.12.30| #1
     

    Ok mo mi metto a fare un sistema di gestione del codice perchè così non se pò annà avanti :P

     
  2. Avatar
    Felix
    11/05/2010 21.01.15| #2
     

    Hai provato anche JQuery UI ? Io su JS sono assai schiappa, mi trovo meglio su Winform/WPF, però ho fatto delle prove e mi è sembrata una gran figata...

     
  3. 11/05/2010 21.05.34| #3
     

    zizzizi (vedi post precedenti): jQuery UI è ottimo per fare un sacco di cose!
    L'accordion mi piace parecchio..date picker...e il sistema di drag & drop.
    Ultimamente mi sto appassionando agli 'effetti speciali' che ho tralasciato stupidamente: un pò di movimento fa bene anche all'interazione!

    Cmq stasera missà che faccio sto sistema di gestione di snipplet ...stay tuned

     
  4. 13/05/2010 18.52.15| #4
     

    OOOoooooOO Finalmente il codice si vede come voglio io