Fuori programma
24-02-2010 14.42.08 GMT+02La battaglia di aNobii (parte 2)La mia avventura con aNobii e le sue risorse continua (e non finisce nemmeno!)

Prima di riprendere il racconto, comunico a chi fosse interessato che ho creato una form che consente di generare il codice della weblet aNobii Filtered Blog Badge (così l'ho battezzata) già pronto da incollare nelle proprie pagine...

Nella precedente puntata avevo concluso con il proposito di fare reverse engineering sniffando le chiamate AJAX di aNobii per capire come facesse ad estrarre i libri secondo un dato tag.

Il risultato è stato che in corrispondenza della selezione di uno degli item del tag cloud (la nuvola) ho rilevato la seguente chiamata HTTP in uscita

http://www.aNobii.com/InternalAPI/html/live/book-shelf/public-view-shelf-ajax?personId=cosimo&shelfView=list_view& sort=1&filter=filter_tags&selected=4&page=1&rows=3&public=0&product=1

provando la chiamata in una finestra del browser ho visto che effettivamente il risultato era quello voluto: la lista degli item con il filtro che mi interessava e con una serie di altre informazioni legate al mio profilo (voto, etc...).
Più nel dettaglio, si tratta di una chiamata HTTP request con una serie di parametri. Il primo è personId che è appunto lo user name, shelfView è legato alla modalità con cui si vuole presentare la lista (c'è la modalità scaffale, il mosaico di copertine, etc...). A me questo non interessava perchè sapendo di ottenere un output HTML che comunque avrei dovuto filtrare con una regular expression, era sufficiente lavorare sempre la stessa formattazione, per cui l'una valeva l'altra...ovviamente, il limite di questa soluzione è che quando aNobii cambierà formattazione HTML del suo output la regular expression andrà rivista. Il parametro sort riguarda l'ordinamento ed in questo caso andava bene così (più recentemente inseriti), filter era quello che mi interessava: con il valore filter_tags filtriamo appunto per tag (non escludo che in futuro possa sperimentare altri tipi di filtro). selected è il codice del tag su cui si vuole filtrare (non sembra si possano usare filtri composti da una combinazione di tag). page è la pagina che si vuole caricare, rows non sembra funzionare, public dovrebbe essere il comando per visualizzare solo libri con l'attributo pubblico se valorizzato a 1. Infine, product non è ben chiaro neanche lui a cosa serve.

Sembrava che mi stessi avviando verso il lieto fine; implemento le regular expression, parametrizzo il codice per nome utente, tag, pagina (con tanto di meccanismo di gestione delle mie pagine da 5 item contro i 12 di aNobii, cosa che richiederà di fare due chiamate nei casi in cui sono "a cavallo" tra una pagina e l'altra, ad es. sulla terza pagina della weblet)...comincio a provare in locale la mia funzione HTTP request (dovrò anch'io implementare la weblet in ajax per non ricaricare tutta la pagina ospite ad ogni cambio pagina della weblet) e noto una serie di risultati che non combaciano con quelli della chiamata sniffata.
In un primo momento non capisco nemmeno la logica che può differenziare i due set di risultati. Poi, vedo che i libri risultanti dalla chiamata del mio codice sono meno di quelli attesi e sono tutti in inglese...faccio una rapida verifica per confermare un sospetto: apro un browser diverso da quello che uso normalmente e la mia pagina di aNobii senza essermi autenticato presenta una lista molto simile...c'è una sola spiegazione: l'interazione con aNobii utilizza un cookie che trasporta il settaggio di un filtro distinto da quello dei tag e che agisce sulla lingua dei libri visualizzati.
In effetti, alla prima navigazione sul loro sito appare in fondo alla pagina la possibilità di impostarlo, ma chi si ricordava...

Usando Safari, che ha ottimi strumenti di supporto agli sviluppatori web, tra cui un cookie explorer, identifico il cookie contenente sia l'id di sessione che i filtri sulla lingua. Questi cookie vanno gestiti esplicitamente; non va dimenticato che ad interagire con aNobii è la mia applicazione web (che non ha sessione) e non un browser interattivo. Integro allora il mio codice con una chiamata esplorativa che mi consente di estrarre prima il cookie di sessione, e salvarlo in una proprietà della mia classe

_cookies = response.Headers["Set-Cookie"].ToString(); 

nelle successive richieste compongo due cookie: uno contenente la sessione già salvata ed un altro con il mio filtro (per ora impostato solo su italiano ed inglese)

        /// <summary>
        /// Sets the session cookie in the request still in preparation. 
        /// Session is retrieved from previous request 
        /// (if this is not the first one) 
        /// using the property <see cref="_cookies">_cookies</see>.
        /// </summary>
        /// <param name="request">Request object where cookies 
        /// are to be injected.</param>
        private void _setCookie(HttpWebRequest request)
        {
            if (string.IsNullOrEmpty(_cookies))
                return; // this is the first request

            // looks for the session cookie and sets also the language cookie
            string[] items = _cookies.Split(';');
            request.CookieContainer = new CookieContainer(1);
            for (int i = 0; i < items.Length; i++)
            {
                if (items[i].StartsWith("PHPSESSID"))
                {
                    // session retrived from the first request header
                    string[] cookieH = items[i].Split('=');
                    Cookie cookie = new Cookie(cookieH[0], 
                        cookieH[1], "/", ".aNobii.com");
                    request.CookieContainer.Add(cookie);
                    // language in the form <lang Number>[,<lang Number>]{n} 
                    // in our case, 1 (eng) and 11 (ita)
                    cookie = new Cookie("language_book", 
                         "1%2C11", "/", "www.aNobii.com");
                    request.CookieContainer.Add(cookie);
                    return;
                }
            }

        }

Il mio metodo HTTP era pronto e funzionava finalmente, ma non era ancora finita.

Mancava un ultimo fondamentale dettaglio: questa architettura così com'era, online non avrebbe girato. Se infatti volevo rilasciare (come poi ho fatto) un pezzetto di codice HTML/CSS/JavaScript/AJAX che chiunque potesse incollare in qualunque pagina, anche un blog, senza richiedere alcuna installazione sul proprio server, l'interazione JavaScript con il metodo appena sviluppato e posto sul mio server avrebbe causato un problema di sicurezza cross-domain. Da un po' di anni a questa parte, i browser  impediscono che le chiamate HttpRequest possano avvenire su domini diversi da quelli della pagina ospite per impedire attacchi di reflected cross-site scripting...mi si prospettavano dunque un paio di soluzioni...

...to be continued

data modifica 01-03-2010 01.02.56 GMT+02

#

CommentiFeed RSS 2.0 di : Fuori programma, commenti a "La battaglia di aNobii (parte 2)"
1) roberta (fairydafne.blogspot.it)13-06-2013 13.06.19ciao! le ho tentate tutte ma non riesco ad avere il badge per il blog, nè dal sito dato che mi rimanda sempre al login nè col tuo servizio inserendo l'id utente e l'id tag mi aiuteresti per favore? grazie
Aggiungi un commento
Mittente : *
Email : 
Web : 
Testo del messaggio : * 
Codice di validazione : * 

otherbit, by Cosimo Carbonelli m. info@otherbit.com p.i. 11743080159
made with OtherWeb