Vediamo adesso come implementare un servizio di Web API generico, utilizzando l’applicazione in esecuzione in un server nel cloud.
Sessioni REST ed evento onCommand #
Il cuore dell’implementazione di un servizio di Web API in un’applicazione Instant Developer Cloud consiste nell’evento app.onCommand, che viene notificato quando l’applicazione viene richiamata specificando mode=rest nella query string.
Ad esempio, l’applicazione di esempio ToBuy può venire aperta in un browser tramite l’URL https://prod3-pro-gamma.instantdevelopercloud.com/ToBuyApp. Quando il browser effettua la chiamata, viene creata nel server una sessione browser che inizia notificando l’evento app.onStart.
L’applicazione ToBuy permette di caricare le immagini dei prodotti, quindi espone una Web API che può essere invocata dall’applicazione in esecuzione nei dispositivi quando viene scattata una foto. L’endpoint della WebAPI sarà il medesimo dell’applicazione e la query string deve contenere mode=rest, come mostrato nell’esempio seguente: https://prod3-pro-gamma.instantdevelopercloud.com/ToBuyApp?mode=rest&fn=xyz. Effettuando la chiamata, si vedrà il messaggio no files che indica il fatto che non sono stati rilevati file da caricare.
L’invocazione dell’applicazione indicando mode=rest sulla query string crea una nuova sessione nel server, una sessione di tipo REST che, appunto, serve per identificare il comando richiesto dalla Web API, per eseguirlo e per restituire i risultati al chiamante; a questo punto la sessione viene terminata. Il ciclo di vita di una sessione REST deve essere il più breve possibile in quanto ogni chiamata deve gestire una singola richiesta, nel minor tempo possibile, in modalità stateless.
L’evento onCommand specifica il parametro request che contiene tutti i dati passati dal chiamante, fra i quali:
- request.query: oggetto JavaScript che contiene i parametri query string della chiamata.
- request.headers: oggetto JavaScript che contiene gli header della chiamata.
- request.cookies: oggetto JavaScript che contiene i cookie della chiamata.
- request.params: oggetto JavaScript che contiene i campi della form se la chiamata è di tipo post.
- request.files: array di oggetti File caricati nel server dalla richiesta post di tipo multipart. I file caricati vengono salvati nella cartella uploaded del file system.
- request.protocol, request.host, request.url, request.method: proprietà della chiamata.
- request.isBot: vale true se la sessione REST è stata attivata a seguito di una chiamata da parte di un crawler di un motore di ricerca.
Nota: oltre a mode=rest sulla query string, esistono altre circostanze per cui viene chiamato l’evento onCommand di un’applicazione; potrebbe quindi essere necessario tenerne conto nel codice di gestione dell’evento. In particolare:
- Nel caso di un’applicazione installata in un dispositivo, se viene attivata tramite un link che contiene il protocollo personalizzato dell’app. Questo caso si può distinguere testando app.runsLocally().
- Nel caso di una chiamata da parte di un crawler di un motore di ricerca. Questo caso si distingue testando request.isBot.
- Nel caso di una chiamata di tipo Web API ODATA, illustrata nei paragrafi seguenti. Per distinguere questo caso è possibile testare app.isWebApiRequest().
Rispondere ad una richiesta #
La risposta ad una chiamata REST deve essere inviata entro 60 secondi (intervallo personalizzabile modificando app.sessionTimeout). Per rispondere occorre utilizzare il metodo app.sendResponse che consente di inviare un codice di risposta http e la risposta vera e propria, restituendo una stringa, un array buffer o un oggetto File. Specificando altri tipi di dati, essi verranno convertiti in stringa. Tramite il parametro options è infine possibile indicare il content-type della risposta e inviare un elenco di header di risposta.
Prima di rispondere può essere opportuno verificare i token di autenticazione ed eventualmente gestire o cancellare eventuali file caricati. Si noti che è possibile utilizzare app.sendResponse una sola volta e che dopo averlo utilizzato la sessione viene terminata. Deve pertanto essere l’ultima operazione eseguita dal codice dell’evento onCommand.
Vediamo adesso un semplice esempio di gestione di una risposta per una Web API che permette di sapere se il server è attivo e funzionante.
App.Session.prototype.onCommand = function (request)
{
try {
if (app.isWebApiRequest()) {
// Gestione Web API ODATA
}
else if (app.runsLocally()) {
// Gestione url protocol offline
}
else if (request.isBot) {
// Gestione crawler motore di ricerca
}
else if (request.query.cmd === "hello") {
// Gestione comando hello
app.sendResponse(200, "Hello. Local time is: " +
app.locale.now().format("LLL"));
}
else {
app.sendResponse(401, "Unknown command");
}
}
catch(ex) {
app.sendResponse(500);
}
};
Gestire i file caricati #
Un importante caso d’uso delle sessioni REST è quello di accettare file caricati da dispositivi, come ad esempio fotografie, documenti, eccetera. A tal fine, le sessioni REST sono predisposte per salvare sul disco del server, nella cartella uploaded del file system, i file che vengono inviati nella richiesta REST se essa è in formato multipart.
Per salvare i file su disco, non vengono usati i loro nomi originali, che vengono invece sostituiti da nomi di file generati in formato guid 36. In questo modo non è possibile per chi carica i file sapere con quale nome verranno salvati.
I file caricati vengono passati all’evento onCommand tramite l’array request.files ed è quindi possibile manipolarli, cancellarli, spostarli in sottocartelle o in ogni altro modo. Si ricorda che la directory uploaded e le sue sotto-directory mantengono i file nella cache dei browser per un anno. Sono quindi la destinazione ideale per documenti caricati che devono permanere nelle cache dei browser.
La dimensione massima accettabile per un file caricato è di 50 MB.
Oltre a gestire i file caricati, una Web API può anche servire file al richiedente, specificando l’oggetto File da servire come secondo parametro del metodo app.sendResponse. In questi casi si consiglia di aggiungere l’header Cache-Control alla risposta in modo da poter fissare per un determinato tempo il file nella cache del dispositivo chiamante.
Questa modalità di servire i file può rappresentare un metodo di sicurezza estremo per garantire la massima privacy ai file del proprio file system. Normalmente non è possibile accedere ai file pubblici del server perché i nomi sono sconosciuti, ma una volta conosciuto il link, questo può essere comunicato ad altri.
Se invece il file viene servito tramite una Web API, inviandolo a partire da un file privato, esso non può essere acceduto se non tramite la Web API stessa, che può quindi effettuare tutti i controlli del caso sui parametri della richiesta, ad esempio su un token di autenticazione a scadenza ravvicinata.
Gestire i crawler dei motori di ricerca #
Se l’URL dell’applicazione viene sottoposta ad un motore di ricerca, oppure viene linkata in un sito sottoposto ad un motore di ricerca, il crawler del motore inizierà a contattarla per recuperarne i contenuti da indicizzare.
In questi casi il framework di Instant Developer Cloud riconosce che la chiamata all’applicazione non proviene da un browser vero e proprio, quindi, invece che servire i file JavaScript che servono per iniziare la sessione browser, attiva una sessione REST per dare la possibilità all’applicazione di rispondere alla richiesta del crawler con un contenuto testuale.
Solitamente la richiesta viene gestita caricando una lista di dati o il dettaglio di un documento. Questi dati vengono usati per costruire una pagina HTML fittizia che viene infine data come risposta alla richiesta del crawler e, in questo modo, il motore di ricerca potrà indicizzare le informazioni in essa contenute.
Quando l’utente cerca le informazioni tramite il motore di ricerca, verrà utilizzato come link di attivazione della pagina proprio quello dell’applicazione stessa, che, questa volta, attiverà una sessione browser vera e propria.
Per maggiori informazioni su queste funzionalità, vedi il paragrafo Esempio di indicizzazione sui motori di ricerca.
Testare la Web API esposta #
Per testare le Web API si consiglia di utilizzare prima l’applicazione in anteprima nell’IDE e poi l’applicazione installata in un server di produzione.
Per testare l’applicazione in anteprima nell’IDE è necessario generare un link che rappresenta l’endpoint di connessione della Web API esposto dall’applicazione in anteprima. A tal fine si consiglia di inserire nell’evento app.onStart il seguente codice:
if (app.inPreview()) {
let urlParts = app.requestConnectionUrl().split("/");
let url = urlParts[0] + "//" + urlParts[2] + "/" + urlParts[6] + "/" +
urlParts[7] + "/" + urlParts[8] + "?mode=rest";
console.log(“WebAPI endpoint”, url);
}
Lanciando l’applicazione in anteprima, nella console dell’IDE apparirà il link che rappresenta l’endpoint specifico della Web API per questa sessione di anteprima. A questo punto è possibile utilizzare questa informazione per effettuare le chiamate da un altro browser oppure tramite PostMan o altri software di test per le Web API.
Durante il test della Web API nell’IDE si deve considerare che non viene generata una sessione REST aggiuntiva, ma la sessione utilizzata è la stessa sessione browser che mostra l’interfaccia utente dell’anteprima. È quindi possibile interagire con le videate aperte e con la console di debug.
La seconda fase di test consiste nell’installazione dell’applicazione in un server di produzione per poter testare l’endpoint effettivo. In questa fase si consiglia di attivare il log strutturato delle sessioni e di inserire nell’evento onCommand la riga di codice che dà il nome alla sessione, come la seguente:
app.sessionName = "(A) API request " + parseInt(Math.random() * 100000);
In questo modo ogni chiamata al server verrà aggiunta al log strutturato in una sessione separata.