Skip to content
Mithril.js 2
Main Navigation GuidaAPI

Italiano

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Polski
Türkçe
čeština
magyar

Italiano

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Polski
Türkçe
čeština
magyar

Aspetto

Sidebar Navigation

API

API principale

m(selector, attributes, children)

render(element, vnodes)

mount(root, component)

route(root, defaultRoute, routes)

request(options)

parseQueryString(string)

buildQueryString(object)

buildPathname(object)

parsePathname(string)

trust(html)

fragment(attrs, children)

redraw()

censor(object, extra)

API opzionale

stream()

Guida

In questa pagina

request(options) ​

Descrizione ​

Effettua richieste XHR (ovvero AJAX) e restituisce una promise.

javascript
m.request({
  method: 'PUT',
  url: '/api/v1/users/:id',
  params: { id: 1 },
  body: { name: 'test' },
}).then(function (result) {
  console.log(result);
});

Firma ​

promise = m.request(options)

ArgomentoTipoRichiestoDescrizione
optionsObjectSìLe opzioni da passare per la richiesta.
options.methodStringNoIl metodo HTTP da utilizzare. Questo valore dovrebbe essere uno dei seguenti: GET, POST, PUT, PATCH, DELETE, HEAD o OPTIONS. Il valore predefinito è GET.
options.urlStringSìL'URL a cui inviare la richiesta, opzionalmente interpolato con valori da options.params.
options.paramsObjectNoI dati da interpolare nell'URL e/o da serializzare nella stringa di query.
options.bodyObjectNoI dati da serializzare nel corpo della richiesta (per metodi diversi da GET).
options.asyncBooleanNoIndica se la richiesta deve essere eseguita in modo asincrono. Il valore predefinito è true.
options.userStringNoUn nome utente per l'autenticazione HTTP. Il valore predefinito è undefined.
options.passwordStringNoUna password per l'autenticazione HTTP. Il valore predefinito è undefined. Questa opzione è fornita per la compatibilità con XMLHttpRequest, ma è consigliabile evitarne l'uso perché invia la password in testo semplice sulla rete.
options.withCredentialsBooleanNoIndica se inviare cookie a domini di terze parti. Il valore predefinito è false.
options.timeoutNumberNoIl tempo massimo in millisecondi che una richiesta può impiegare prima di essere automaticamente interrotta. Il valore predefinito è undefined.
options.responseTypeStringNoIl tipo previsto per la risposta. Il valore predefinito è "" se extract è definito, altrimenti "json".
options.configxhr = Function(xhr)NoEspone l'oggetto XMLHttpRequest sottostante per la configurazione a basso livello e la sostituzione opzionale (restituendo un nuovo XHR).
options.headersObjectNoIntestazioni da aggiungere alla richiesta prima dell'invio (applicate immediatamente prima di options.config).
options.typeany = Function(any)NoUn costruttore da applicare a ogni oggetto nella risposta.
options.serializestring = Function(any)NoUn metodo di serializzazione da applicare al body.
options.deserializeany = Function(any)NoUn metodo di deserializzazione da applicare a xhr.response o al xhr.responseText normalizzato.
options.extractany = Function(xhr, options)NoUn hook per specificare come leggere la risposta di XMLHttpRequest. Utile per elaborare i dati di risposta, leggere le intestazioni e i cookie. Per impostazione predefinita, questa è una funzione che restituisce options.deserialize(parsedResponse), generando un'eccezione quando il codice di stato della risposta del server indica un errore o quando la risposta non è sintatticamente valida. Se viene fornito un callback personalizzato, il parametro xhr è l'istanza XMLHttpRequest utilizzata per la richiesta e options è l'oggetto passato alla chiamata m.request. Inoltre, deserialize verrà ignorato e il valore restituito dal callback extract verrà lasciato così com'è quando la promise si risolve.
options.backgroundBooleanNoSe false, ridisegna i componenti montati al termine della richiesta. Se true, non lo fa. Il valore predefinito è false.
restituiscePromiseUna promise che si risolve nei dati di risposta, dopo essere stati elaborati dai metodi extract, deserialize e type. Se il codice di stato della risposta indica un errore, la promise viene rifiutata, ma questo può essere evitato impostando l'opzione extract.

promise = m.request(url, options)

ArgomentoTipoRichiestoDescrizione
urlStringSìL'URL a cui inviare la richiesta. options.url sovrascrive questo valore quando è presente.
optionsObjectNoLe opzioni di richiesta da passare.
restituiscePromiseUna promise che si risolve nei dati di risposta, dopo che sono stati elaborati tramite i metodi extract, deserialize e type.

Questa seconda forma è per lo più equivalente a m.request(Object.assign({url: url}, options)), solo che non dipende internamente dalla funzione globale ES6 Object.assign.

Come leggere le firme

Come funziona ​

L'utility m.request è un wrapper semplificato per XMLHttpRequest e consente di effettuare richieste HTTP a server remoti per salvare e/o recuperare dati da un database.

javascript
m.request({
  method: 'GET',
  url: '/api/v1/users',
}).then(function (users) {
  console.log(users);
});

Una chiamata a m.request restituisce una promise e attiva un ridisegno al termine della sua catena di promise.

Per impostazione predefinita, m.request presuppone che la risposta sia in formato JSON e la analizza in un oggetto JavaScript (o in un array).

Se il codice di stato della risposta HTTP indica un errore, la promise restituita verrà rifiutata. Fornire un callback extract impedirà il rifiuto della promise.

Utilizzo tipico ​

Ecco un esempio di un componente che utilizza m.request per recuperare dati da un server.

javascript
var Data = {
  todos: {
    list: [],
    fetch: function () {
      m.request({
        method: 'GET',
        url: '/api/v1/todos',
      }).then(function (items) {
        Data.todos.list = items;
      });
    },
  },
};

var Todos = {
  oninit: Data.todos.fetch,
  view: function (vnode) {
    return Data.todos.list.map(function (item) {
      return m('div', item.title);
    });
  },
};

m.route(document.body, '/', {
  '/': Todos,
});

Supponiamo che una richiesta all'URL del server /api/items restituisca un array di oggetti in formato JSON.

Quando m.route viene chiamato alla fine, il componente Todos viene inizializzato. Viene chiamato oninit, che a sua volta chiama m.request. Questo recupera un array di oggetti dal server in modo asincrono. "Asincrono" significa che JavaScript continua a eseguire altro codice mentre attende la risposta dal server. In questo caso, significa che fetch restituisce e il componente viene renderizzato con l'array vuoto originale in Data.todos.list. Una volta completata la richiesta al server, l'array di oggetti items viene assegnato a Data.todos.list e il componente viene renderizzato nuovamente, producendo un elenco di <div> contenenti i titoli di ciascun todo.

Gestione degli errori ​

Quando una richiesta (non file:) restituisce un codice di stato diverso da 2xx o 304, viene rifiutata con un errore. Questo errore è una normale istanza di Error, ma con alcune proprietà speciali.

  • error.message è impostato sul testo della risposta grezza.
  • error.code è impostato sul codice di stato stesso.
  • error.response è impostato sulla risposta analizzata, utilizzando options.extract e options.deserialize come avviene con le normali risposte.

Questo è utile in molti casi in cui si possono gestire gli errori. Se si desidera rilevare se una sessione è scaduta, si può fare if (error.code === 401) return promptForAuth().then(retry). Se si raggiunge il meccanismo di throttling di un'API e questa restituisce un errore con un "timeout": 1000, si può fare un setTimeout(retry, error.response.timeout).

Icone di caricamento e messaggi di errore ​

Ecco una versione ampliata dell'esempio precedente che implementa un indicatore di caricamento e un messaggio di errore:

javascript
var Data = {
  todos: {
    list: null,
    error: '',
    fetch: function () {
      m.request({
        method: 'GET',
        url: '/api/v1/todos',
      })
        .then(function (items) {
          Data.todos.list = items;
        })
        .catch(function (e) {
          Data.todos.error = e.message;
        });
    },
  },
};

var Todos = {
  oninit: Data.todos.fetch,
  view: function (vnode) {
    return Data.todos.error
      ? [m('.error', Data.todos.error)]
      : Data.todos.list
      ? [
          Data.todos.list.map(function (item) {
            return m('div', item.title);
          }),
        ]
      : m('.loading-icon');
  },
};

m.route(document.body, '/', {
  '/': Todos,
});

Ci sono alcune differenze tra questo esempio e il precedente. Qui, Data.todos.list è inizialmente null. Inoltre, c'è un campo aggiuntivo error per contenere un messaggio di errore e la vista del componente Todos è stata modificata per visualizzare un messaggio di errore se presente, oppure un'icona di caricamento se Data.todos.list non è un array.

URL dinamici ​

Gli URL delle richieste possono contenere delle interpolazioni:

javascript
m.request({
  method: 'GET',
  url: '/api/v1/users/:id',
  params: { id: 123 },
}).then(function (user) {
  console.log(user.id); // logs 123
});

Nel codice sopra, :id viene sostituito con i dati dall'oggetto params, e la richiesta diventa GET /api/v1/users/123.

Le interpolazioni vengono ignorate se non ci sono dati corrispondenti nella proprietà params.

javascript
m.request({
  method: 'GET',
  url: '/api/v1/users/foo:bar',
  params: { id: 123 },
});

Nel codice sopra, la richiesta diventa GET /api/v1/users/foo:bar?id=123.

Interruzione delle richieste ​

A volte, può essere utile interrompere una richiesta. Ad esempio, in un widget di autocompletamento/typeahead, si desidera garantire che solo l'ultima richiesta venga completata, poiché in genere gli autocompletatori inviano diverse richieste mentre l'utente digita e le richieste HTTP possono essere completate in ordine sparso a causa della natura imprevedibile delle reti. Se un'altra richiesta termina dopo l'ultima inviata, il widget visualizzerebbe dati meno pertinenti (o potenzialmente errati) rispetto a se l'ultima richiesta inviata fosse completata per ultima.

m.request() espone il suo oggetto XMLHttpRequest sottostante tramite il parametro options.config, che consente di mantenere un riferimento a tale oggetto e chiamare il suo metodo abort quando necessario:

javascript
var searchXHR = null;
function search() {
  abortPreviousSearch();

  m.request({
    method: 'GET',
    url: '/api/v1/users',
    params: { search: query },
    config: function (xhr) {
      searchXHR = xhr;
    },
  });
}
function abortPreviousSearch() {
  if (searchXHR !== null) searchXHR.abort();
  searchXHR = null;
}

Caricamento di file ​

Per caricare file, è necessario prima ottenere un riferimento a un oggetto File. Il modo più semplice per farlo è utilizzare un <input type="file">.

javascript
m.render(document.body, [m('input[type=file]', { onchange: upload })]);

function upload(e) {
  var file = e.target.files[0];
}

Lo snippet sopra genera un input file. Se un utente seleziona un file, viene attivato l'evento onchange, che invoca la funzione upload. e.target.files è una lista di oggetti File.

Successivamente, è necessario creare un oggetto FormData per generare una richiesta multipart, che è una richiesta HTTP formattata in modo speciale in grado di inviare dati di file nel corpo della richiesta.

javascript
function upload(e) {
  var file = e.target.files[0];

  var body = new FormData();
  body.append('myfile', file);
}

Successivamente, è necessario chiamare m.request e impostare options.method su un metodo HTTP che utilizza il corpo (ad es. POST, PUT, PATCH) e utilizzare l'oggetto FormData come options.body.

javascript
function upload(e) {
  var file = e.target.files[0];

  var body = new FormData();
  body.append('myfile', file);

  m.request({
    method: 'POST',
    url: '/api/v1/upload',
    body: body,
  });
}

Supponendo che il server sia configurato per accettare richieste multipart, le informazioni sul file saranno associate alla chiave myfile.

Caricamento di più file ​

È possibile caricare più file in un'unica richiesta. In questo modo, il caricamento in batch sarà atomico, ovvero nessun file verrà elaborato se si verifica un errore durante il caricamento, quindi non sarà possibile salvare solo una parte dei file. Se si desidera salvare il maggior numero possibile di file in caso di errore di rete, è consigliabile caricare ogni file in una richiesta distinta.

Per caricare più file, è sufficiente aggiungerli tutti all'oggetto FormData. Quando si utilizza un input file, è possibile ottenere una lista di file aggiungendo l'attributo multiple all'input:

javascript
m.render(document.body, [
  m('input[type=file][multiple]', { onchange: upload }),
]);

function upload(e) {
  var files = e.target.files;

  var body = new FormData();
  for (var i = 0; i < files.length; i++) {
    body.append('file' + i, files[i]);
  }

  m.request({
    method: 'POST',
    url: '/api/v1/upload',
    body: body,
  });
}

Monitoraggio dell'avanzamento ​

A volte, se una richiesta è intrinsecamente lenta (ad es. un caricamento di file di grandi dimensioni), può essere utile visualizzare un indicatore di avanzamento all'utente per segnalare che l'applicazione è ancora in funzione.

m.request() espone il suo oggetto XMLHttpRequest sottostante tramite il parametro options.config, che consente di collegare gestori di eventi all'oggetto XMLHttpRequest:

javascript
var progress = 0;

m.mount(document.body, {
  view: function () {
    return [
      m('input[type=file]', { onchange: upload }),
      progress + '% completed',
    ];
  },
});

function upload(e) {
  var file = e.target.files[0];

  var body = new FormData();
  body.append('myfile', file);

  m.request({
    method: 'POST',
    url: '/api/v1/upload',
    body: body,
    config: function (xhr) {
      xhr.upload.addEventListener('progress', function (e) {
        progress = e.loaded / e.total;

        m.redraw(); // tell Mithril.js that data changed and a re-render is needed
      });
    },
  });
}

Nell'esempio sopra, viene generato un input file. Se l'utente seleziona un file, viene avviato un caricamento e, nel callback config, viene registrato un gestore dell'evento progress. Questo gestore di eventi viene attivato ogni volta che c'è un aggiornamento dell'avanzamento in XMLHttpRequest. Poiché l'evento di avanzamento di XMLHttpRequest non è gestito direttamente dal motore DOM virtuale di Mithril.js, è necessario chiamare m.redraw() per segnalare a Mithril.js che i dati sono cambiati e che è necessario un ridisegno.

Casting della risposta a un tipo ​

A seconda dell'architettura complessiva dell'applicazione, potrebbe essere utile trasformare i dati di risposta di una richiesta in una classe o tipo specifico (ad esempio, per formattare uniformemente i campi data o per avere metodi di supporto).

È possibile passare un costruttore come parametro options.type, e Mithril.js lo istanzierà per ogni oggetto nella risposta HTTP.

javascript
function User(data) {
  this.name = data.firstName + ' ' + data.lastName;
}

m.request({
  method: 'GET',
  url: '/api/v1/users',
  type: User,
}).then(function (users) {
  console.log(users[0].name); // logs a name
});

Nell'esempio sopra, supponendo che /api/v1/users restituisca un array di oggetti, il costruttore User verrà istanziato (cioè chiamato come new User(data)) per ogni oggetto nell'array. Se la risposta restituisse un singolo oggetto, tale oggetto verrebbe utilizzato come argomento body.

Risposte non JSON ​

A volte un endpoint del server non restituisce una risposta JSON: ad esempio, si potrebbe richiedere un file HTML, un file SVG o un file CSV. Per impostazione predefinita, Mithril.js tenta di analizzare una risposta come se fosse in formato JSON. Per sovrascrivere questo comportamento, definire una funzione options.deserialize personalizzata:

javascript
m.request({
  method: 'GET',
  url: '/files/icon.svg',
  deserialize: function (value) {
    return value;
  },
}).then(function (svg) {
  m.render(document.body, m.trust(svg));
});

Nell'esempio sopra, la richiesta recupera un file SVG, non fa nulla per analizzarlo (poiché deserialize restituisce semplicemente il valore così com'è) e quindi renderizza la stringa SVG come HTML attendibile.

Naturalmente, una funzione deserialize può essere più complessa:

javascript
m.request({
  method: 'GET',
  url: '/files/data.csv',
  deserialize: parseCSV,
}).then(function (data) {
  console.log(data);
});

function parseCSV(data) {
  // naive implementation for the sake of keeping example simple
  return data.split('\n').map(function (row) {
    return row.split(',');
  });
}

Ignorando il fatto che la funzione parseCSV sopra non gestisce molti casi che un parser CSV appropriato dovrebbe gestire, il codice sopra registra un array di array.

Le intestazioni personalizzate possono essere utili anche in questo contesto. Ad esempio, se si richiede un SVG, si vorrà probabilmente impostare il tipo di contenuto di conseguenza. Per sovrascrivere il tipo di richiesta JSON predefinito, impostare options.headers su un oggetto di coppie chiave-valore corrispondenti ai nomi e ai valori delle intestazioni di richiesta.

javascript
m.request({
  method: 'GET',
  url: '/files/image.svg',
  headers: {
    'Content-Type': 'image/svg+xml; charset=utf-8',
    Accept: 'image/svg, text/*',
  },
  deserialize: function (value) {
    return value;
  },
});

Recupero dei dettagli della risposta ​

Per impostazione predefinita, Mithril.js tenta di analizzare xhr.responseText come JSON e restituisce l'oggetto analizzato. Può essere utile esaminare una risposta del server in modo più dettagliato ed elaborarla manualmente. Questo può essere fatto passando una funzione options.extract personalizzata:

javascript
m.request({
  method: 'GET',
  url: '/api/v1/users',
  extract: function (xhr) {
    return { status: xhr.status, body: xhr.responseText };
  },
}).then(function (response) {
  console.log(response.status, response.body);
});

Il parametro di options.extract è l'oggetto XMLHttpRequest una volta completata la sua operazione, ma prima che sia stato passato alla catena di promise restituita, quindi la promise potrebbe comunque finire in uno stato rifiutato se l'elaborazione genera un'eccezione.

Emissione di fetch a indirizzi IP ​

A causa del modo (molto semplice) in cui i parametri vengono rilevati negli URL, i segmenti di indirizzo IPv6 vengono confusi con interpolazioni di parametri di percorso e, poiché i parametri di percorso necessitano di un separatore per essere interpolati correttamente, ciò genera un errore.

javascript
// This doesn't work
m.request('http://[2001:db8::990a:cd27:4d9e:79]:8080/some/path', {
  // ...
});

Per aggirare questo problema, è necessario passare la coppia indirizzo IPv6 + porta come parametro.

javascript
m.request('http://:host/some/path', {
  params: { host: '[2001:db8::990a:cd27:4d9e:79]:8080' },
  // ...
});

Questo non è un problema con gli indirizzi IPv4, che possono essere utilizzati normalmente.

javascript
// This will work as you expect
m.request('http://192.0.2.15:8080/some/path', {
  // ...
});

Perché JSON invece di HTML ​

Molti framework lato server forniscono un motore di visualizzazione che interpola i dati del database in un template prima di servire HTML (al caricamento della pagina o tramite AJAX) e quindi utilizzano jQuery per gestire le interazioni dell'utente.

Al contrario, Mithril.js è un framework progettato per applicazioni client pesanti, che in genere scaricano template e dati separatamente e li combinano nel browser tramite JavaScript. Eseguire il templating pesante nel browser può portare vantaggi, come la riduzione dei costi operativi liberando risorse del server. Separare i template dai dati consente anche di memorizzare nella cache il codice del template in modo più efficace e migliora la riusabilità del codice tra diversi tipi di client (ad es. desktop, mobile). Un altro vantaggio è che Mithril.js abilita un paradigma di sviluppo dell'interfaccia utente retained mode, che semplifica notevolmente lo sviluppo e la manutenzione di interazioni utente complesse.

Per impostazione predefinita, m.request si aspetta che i dati di risposta siano in formato JSON. In una tipica applicazione Mithril.js, tali dati JSON vengono solitamente consumati da una vista.

Si dovrebbe evitare di tentare di renderizzare HTML dinamico generato dal server con Mithril. Se si ha un'applicazione esistente che utilizza un sistema di templating lato server e si desidera ri-architettarla, è importante decidere prima se lo sforzo è fattibile. La migrazione da un'architettura server pesante a un'architettura client pesante è generalmente uno sforzo considerevole e comporta il refactoring della logica dai template in servizi dati logici (e il test correlati).

I servizi dati possono essere organizzati in vari modi a seconda della natura dell'applicazione. Le architetture RESTful sono popolari tra i fornitori di API, mentre le architetture orientate ai servizi sono spesso richieste in presenza di flussi di lavoro altamente transazionali.

Perché XHR invece di fetch ​

fetch() è una nuova API Web per recuperare risorse dai server, simile a XMLHttpRequest.

m.request di Mithril.js utilizza XMLHttpRequest invece di fetch() per vari motivi:

  • fetch non è ancora completamente standardizzato e potrebbe essere soggetto a modifiche delle specifiche.
  • Le chiamate XMLHttpRequest possono essere interrotte prima che si risolvano (ad es. per evitare race condition nelle interfacce utente di ricerca istantanea).
  • XMLHttpRequest fornisce hook per i gestori di avanzamento per le richieste a lunga esecuzione (ad es. caricamenti di file).
  • XMLHttpRequest è supportato da tutti i browser, mentre fetch() non è supportato da Internet Explorer e dalle versioni precedenti di Android (prima della 5.0 Lollipop).

Attualmente, a causa della mancanza di supporto del browser, fetch() richiede generalmente un polyfill, che è di oltre 11kb non compresso, quasi tre volte più grande del modulo XHR di Mithril.js.

Nonostante sia molto più piccolo, il modulo XHR di Mithril.js supporta molte funzionalità importanti e non banali da implementare, come interpolazione URL e serializzazione della query string, oltre alla sua capacità di integrarsi perfettamente nel sottosistema di autoredrawing di Mithril.js. Il polyfill fetch non supporta nessuna di queste funzionalità e richiede librerie e codice standard aggiuntivi per raggiungere lo stesso livello di funzionalità.

Inoltre, il modulo XHR di Mithril.js è ottimizzato per gli endpoint basati su JSON e rende il caso più comune appropriatamente conciso, ovvero m.request(url).

L'API fetch() presenta alcuni vantaggi tecnici rispetto a XMLHttpRequest in casi non comuni:

  • fornisce un'API di streaming (nel senso di "video streaming", non nel senso di programmazione reattiva), che consente una migliore latenza e un minore consumo di memoria per risposte molto grandi (a costo di complessità del codice).
  • si integra con la Service Worker API, che fornisce un ulteriore livello di controllo su come e quando avvengono le richieste di rete. Questa API consente inoltre l'accesso alle notifiche push e alle funzionalità di sincronizzazione in background.

In scenari tipici, lo streaming non fornirà vantaggi significativi in termini di prestazioni, poiché in genere non è consigliabile scaricare megabyte di dati in primo luogo. Inoltre, i guadagni di memoria derivanti dal riutilizzo ripetuto di piccoli buffer possono essere compensati o annullati se comportano eccessive ridisegni del browser. Per questi motivi, la scelta dello streaming fetch() rispetto a m.request è consigliata solo per applicazioni estremamente intensive in termini di risorse.

Evitare anti-pattern ​

Le promise non sono i dati di risposta ​

Il metodo m.request restituisce una Promise, non i dati di risposta. Non può restituire tali dati direttamente perché una richiesta HTTP potrebbe richiedere molto tempo per essere completata (a causa della latenza di rete) e, se JavaScript attendesse, bloccherebbe l'applicazione fino a quando i dati non fossero disponibili.

javascript
// EVITARE
var users = m.request('/api/v1/users');
console.log('list of users:', users);
// `users` is NOT a list of users, it's a promise

// PREFERIBILE
m.request('/api/v1/users').then(function (users) {
  console.log('list of users:', users);
});
Pager
Pagina precedenteroute(root, defaultRoute, routes)
Pagina successivaparseQueryString(string)

Rilasciato sotto la licenza MIT.

Copyright (c) 2024 Mithril Contributors

https://mithril.js.org/request.html

Rilasciato sotto la licenza MIT.

Copyright (c) 2024 Mithril Contributors