Dart: una client library per l’API di Diffbot

Di - 25 October 2013 - in
Post image for Dart: una client library per l’API di Diffbot

Nelle ultime settimane abbiamo osservato un notevole incremento di casi di utilizzo di Dart in produzione, a testimonianza dell’affidabilità e della robustezza di questo nuovo linguaggio e della produttività che l’ambiente di sviluppo garantisce. Avendo sempre creduto nelle potenzialità di Dart, anch’io come molti ho da tempo iniziato ad utilizzarlo in produzione, a casa e in ufficio, impiegandolo in un primo momento per semplici script o applicazioni di supporto al workflow personale ed aziendale, e impiegandolo successivamente in ambiti più complessi..

In questo articolo voglio presentarvi una libreria che ho sviluppato di recente e che consiste in una client library per l’API di Diffbot, un servizio online che data l’URL di un articolo, di un blog/webzine o di un prodotto, ne estrae i campi principali; nel caso di un articolo, Diffbot ne estrae autore, data, titolo, testo, media, tag e altro ancora, nel caso di un blog ne estrae gli elementi presenti sulla frontpage e i dati relativi ad ogni singolo elemento e nel caso di un prodotto ne estrae titolo, prezzo, sconto, e altre informazioni disponibili.

Questa libreria è disponibile su Pub, il gestore di pacchetti, quindi per importarla nel nostro progetto possiamo definire manualmente la dipendenza editando il file pubspec.yaml:

dependencies:
    diffbot: any

oppure sceglierla cliccando su Add… dall’editor di pubspec.yaml incluso nel Dart Editor (verrà mostrata una finestra per esplorare tutte le librerie disponibili).

Come vedremo in dettaglio fra poco, è possibile utilizzare la libreria sia in applicazioni da riga di comando sia in applicazioni web, quindi, gli import statement saranno rispettivamente:

import 'package:diffbot/diffbot_console.dart'; // app da riga di comando

import 'package:diffbot/diffbot_browser.dart'; //web app

Assumiamo ora di voler ottenere le informazioni su un articolo, prendiamo ad esempio l’ultimo articolo di Ars Technica dedicato ad Android; vogliamo sapere chi è l’autore, qual è il titolo e la data di pubblicazione. Dobbiamo innanzitutto inizializzare un oggetto Client() e passargli come unico argomento un token per l’accesso all’API di Diffbot — un token può essere ottenuto gratuitamente alla pagina diffbot.com/pricing — dopodiché invochiamo il metodo getArticle():

main() {
  var client = new Client('IL_NOSTRO_TOKEN');
  client.getArticle('http://arstechnica.com/gadgets/2013/10/googles-iron-grip-on-android-controlling-open-source-by-any-means-necessary/').then((Article articolo) {
    print(articolo.author);
    print(articolo.title);
    print(articolo.date);
  });
}

Il risultato sarà:

Ron Amadeo
Google’s iron grip on Android: Controlling open source by any means necessary
2013-10-21 01:00:00.000

Analogamente, per ottenere informazioni sulla pagina principale di un blog utilizzeremo il metodo getFrontpage(), mentre per ottenere informazioni su un prodotto utilizzeremo getProduct(). La documentazione ufficiale su queste API è disponibile all’indirizzo diffbot.com/products/automatic.

Ora vediamo, lato sviluppatore, un paio di caratteristiche di questa libreria da cui possiamo trarre spunti interessanti. Per reperire le informazioni, la libreria ha bisogno di inoltrare una richiesta HTTP all’endpoint dell’API di Diffbot per poi successivamente analizzarne il responso; a questo punto sorgono due problemi critici, strettamente legati fra loro:

  • Misure di sicurezza per la prevenzione di attacchi cross-site
  • Conflitto fra le librerie dart:io e dart:html

Per ragioni di sicurezza, i più diffusi browser moderni bloccano l’inoltro di una richiesta HTTP ad un origine diversa da quella da cui parte la richiesta stessa (cfr: MDN: HTTP access control (CORS) ), quindi incappiamo nel primo dei due problemi. Come si risolve questo problema? Esiste una tecnica, chiamata JSONP (JSON with Padding), che consente ad uno script di comunicare con un’origine esterna sfruttando l’attributo src dell’elemento HTML script. In Javascript l’utilizzo di questa tecnica è estremamente semplice: l’applicazione crea un nuovo elemento HTML script, valorizza l’attributo src con l’endpoint dell’API accodando alla richiesta il parametro &callback=nostraCallback e alla ricezione del responso sarà invocata la funzione nostraCallback che gestirà i dati ricevuti.
Al momento questo comportamento non può essere ottenuto nativamente in Dart, ma c’è bisogno di utilizzare la libreria per l’interoperabilità fra Dart e JS, ovvero js.dart; il codice ha questa struttura:

import 'package:js/js.dart' as js;
main() {
  js.context["nostraCallback"] = new js.Callback.once((js.Proxy data) {
    // qui gestiamo i nostri dati in `data`
  });
  ScriptElement scriptTag = new Element.tag('script');
  // qui abbiamo la nostra richiesta, che termina con
  // &callback=nostraCallback
  scriptTag.src = requestUrl;
  document.body.children.add(scriptTag);
  scriptTag.remove();
}

A questo punto incappiamo nel secondo problema. La libreria js.dart e la classe Element() dipendono da dart:html; le funzioni della libreria devono essere disponibili anche per applicazioni da riga di comando, quindi ci sarebbe anche dart:io fra le dipendenze, ma sappiamo che per ragioni di sicurezza una stessa libreria non può dipendere contemporaneamente da dart:html e dart:io. Il problema si risolve avvalendoci di una interfaccia condivisa che viene estesa in due classi distinte da utilizzare rispettivamente nella console e nel browser. In Dart non esistono (più) oggetti di tipo interface, si usano al loro posto classi astratte, quindi nel nostro caso possiamo utilizzare un oggetto condiviso in cui vengono dichiarati i metodi, che chiamiamo BaseClient e due oggetti che lo estendono e che definiscono i metodi, ovvero Client. La struttura è la seguente:

lib/src/shared/base_client.dart // qui c'è la classe astratta
lib/src/browser/client.dart     // qui c'è l'oggetto Client per il browser
lib/src/console/client.dart     // qui c'è l'oggetto Client per la riga di comando
lib/diffbot_browser.dart        // la libreria per il browser
lib/diffbot_console.dart        // la libreria per la console

Nello specifico, il BaseClient avrà questa forma:

abstract class BaseClient {
  String token;
  final String articleEndpoint = "http://www.diffbot.com/api/article";
  final String frontpageEndpoint = "http://www.diffbot.com/api/frontpage";
  final String productEndpoint = "http://api.diffbot.com/v2/product";

  Future<Article> getArticle({...}); // Dichiarazione dei metodi della classe
  Future<Frontpage> getFrontpage({...});
  Future<Product> getProduct({...});
}

Mentre Client avrà questa forma:

part of diffbot_console;

class Client extends BaseClient {
  final String token;
  Client(this.token);
  Future<Article> getArticle (String url) {
    // definizione del metodo
  }
}

Questo è il motivo per cui abbiamo due percorsi diversi per gli import statement. Concludiamo con alcuni link utili:

Leave a Reply

Claudio d'Angelis Articolo scritto da

Programmatore e studente di Informatica, appassionato di musica, web e sistemi UNIX. Collabora con Googlab dall’Ottobre 2012.

Contatta l'autore

Previous post:

Next post: