Skip to content
Mithril.js 2
Main Navigation GuiaAPI

Português – Brasil

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

Português – Brasil

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

Aparência

Sidebar Navigation

API

API principal

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 Opcional

stream()

Guia

Nesta página

request(options) ​

Descrição ​

Realiza requisições XHR (também conhecidas como AJAX) e retorna uma Promise.

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

Assinatura ​

promise = m.request(options)

ArgumentoTipoObrigatórioDescrição
optionsObjectSimAs opções da requisição a serem passadas.
options.methodStringNãoO método HTTP a ser usado. Este valor deve ser um dos seguintes: GET, POST, PUT, PATCH, DELETE, HEAD ou OPTIONS. O padrão, caso não especificado, é GET.
options.urlStringSimO nome do caminho para enviar a requisição, opcionalmente interpolado com valores de options.params.
options.paramsObjectNãoOs dados a serem interpolados na URL e/ou serializados na query string.
options.bodyObjectNãoOs dados a serem serializados no corpo da requisição (para métodos que suportam corpo, como POST, PUT, etc.).
options.asyncBooleanNãoIndica se a requisição deve ser assíncrona. O padrão é true.
options.userStringNãoUm nome de usuário para autorização HTTP. O padrão, se não especificado, é undefined.
options.passwordStringNãoUma senha para autorização HTTP. O padrão, se não especificado, é undefined. Esta opção é fornecida para compatibilidade com XMLHttpRequest, mas você deve evitar usá-la, pois ela envia a senha em texto simples pela rede.
options.withCredentialsBooleanNãoIndica se os cookies devem ser enviados para domínios de terceiros. O padrão, caso não especificado, é false.
options.timeoutNumberNãoA quantidade de milissegundos que uma requisição pode levar antes de ser automaticamente encerrada. O padrão é undefined.
options.responseTypeStringNãoO tipo esperado da resposta. O padrão é "" se extract estiver definido, "json" caso contrário. Se responseType: "json", ele internamente executa JSON.parse(responseText).
options.configxhr = Function(xhr)NãoExpõe o objeto XMLHttpRequest subjacente para configuração de baixo nível e substituição opcional (retornando um novo XHR).
options.headersObjectNãoCabeçalhos (headers) a serem anexados à requisição antes de enviá-la (aplicados imediatamente antes de options.config).
options.typeany = Function(any)NãoUm construtor a ser aplicado a cada objeto na resposta. O padrão é a função identidade.
options.serializestring = Function(any)NãoUm método de serialização a ser aplicado a body. O padrão é JSON.stringify, ou se options.body for uma instância de FormData ou URLSearchParams, o padrão é a função identidade (ou seja, function(value) {return value}).
options.deserializeany = Function(any)NãoUm método de desserialização a ser aplicado a xhr.response ou xhr.responseText normalizado. O padrão é a função identidade. Se extract estiver definido, deserialize será ignorado.
options.extractany = Function(xhr, options)NãoUm hook para especificar como a resposta XMLHttpRequest deve ser lida. Útil para processar dados de resposta, ler cabeçalhos e cookies. Por padrão, esta é uma função que retorna options.deserialize(parsedResponse), lançando uma exceção quando o código de status da resposta indica um erro ou a resposta é sintaticamente inválida. Se um callback extract personalizado for fornecido, o parâmetro xhr é a instância XMLHttpRequest usada para a requisição, e options é o objeto que foi passado para a chamada m.request. Além disso, deserialize será ignorado e o valor retornado do callback extract será usado diretamente para resolver a Promise.
options.backgroundBooleanNãoSe false, redesenha os componentes montados após a conclusão da requisição. Se true, não redesenha. O padrão é false.
retornaPromiseUma Promise que é resolvida com os dados da resposta, após terem sido processados pelos métodos extract, deserialize e type. Se o código de status da resposta indicar um erro, a Promise é rejeitada, mas isso pode ser evitado definindo a opção extract.

promise = m.request(url, options)

ArgumentoTipoObrigatórioDescrição
urlStringSimO nome do caminho para enviar a requisição. options.url substitui este valor quando presente.
optionsObjectNãoAs opções da requisição a serem passadas.
retornaPromiseUma Promise que é resolvida com os dados da resposta, após terem sido processados pelos métodos extract, deserialize e type.

Esta segunda forma é praticamente equivalente a m.request(Object.assign({url: url}, options)), só que não depende do global ES6 Object.assign internamente.

Como ler assinaturas

Como funciona ​

O utilitário m.request é um wrapper fino em torno de XMLHttpRequest, e permite fazer requisições HTTP para servidores remotos para salvar e/ou recuperar dados de um banco de dados.

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

Uma chamada para m.request retorna uma Promise e aciona um redesenho após a conclusão de sua cadeia de Promise.

Por padrão, m.request assume que a resposta está no formato JSON e a analisa em um objeto JavaScript (ou array).

Se o código de status da resposta HTTP indicar um erro, a Promise retornada será rejeitada. Fornecer um callback extract evitará a rejeição da Promise.

Uso típico ​

Aqui está um exemplo ilustrativo de um componente que usa m.request para recuperar alguns dados de um servidor.

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,
});

Vamos supor que fazer uma requisição para a URL do servidor /api/items retorna um array de objetos no formato JSON.

Quando m.route é chamado na parte inferior, o componente Todos é inicializado. oninit é chamado, que chama m.request. Isso recupera um array de objetos do servidor de forma assíncrona. "Assincronamente" significa que o JavaScript continua executando outro código enquanto espera pela resposta do servidor. Neste caso, significa que fetch retorna, e o componente é renderizado usando o array vazio original como Data.todos.list. Uma vez que a requisição para o servidor é concluída, o array de objetos items é atribuído a Data.todos.list e o componente é renderizado novamente, produzindo uma lista de <div>s contendo os títulos de cada todo.

Tratamento de erros ​

Quando uma requisição não file: retorna com qualquer status diferente de 2xx ou 304, ela é rejeitada com um erro. Este erro é uma instância normal de Error, mas com algumas propriedades especiais.

  • error.message é definido como o texto de resposta bruto.
  • error.code é definido como o próprio código de status.
  • error.response é definido como a resposta analisada, usando options.extract e options.deserialize como é feito com respostas normais.

Isso é útil em muitos casos onde os erros são eles mesmos coisas que você pode contabilizar. Se você quiser detectar se uma sessão expirou - você pode fazer if (error.code === 401) return promptForAuth().then(retry). Se você atingir o mecanismo de limitação de uma API e ele retornar um erro com um "timeout": 1000, você pode fazer um setTimeout(retry, error.response.timeout).

Ícones de carregamento e mensagens de erro ​

Aqui está uma versão expandida do exemplo acima que implementa um indicador de carregamento e uma mensagem de erro:

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,
});

Existem algumas diferenças entre este exemplo e o anterior. Aqui, Data.todos.list é null no início. Além disso, há um campo extra error para armazenar uma mensagem de erro, e a view do componente Todos foi modificada para exibir uma mensagem de erro se existir, ou exibir um ícone de carregamento se Data.todos.list não for um array.

URLs dinâmicas ​

URLs de requisição podem conter interpolações:

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

No código acima, :id é preenchido com os dados do objeto params, e a requisição se torna GET /api/v1/users/123.

Interpolações são ignoradas se não existirem dados correspondentes na propriedade params.

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

No código acima, a requisição se torna GET /api/v1/users/foo:bar?id=123.

Abortando requisições ​

Às vezes, é desejável abortar uma requisição. Por exemplo, em um widget de autocompletar/typeahead, você quer garantir que apenas a última requisição seja concluída, porque normalmente os autocompletadores disparam várias requisições enquanto o usuário digita e as requisições HTTP podem ser concluídas fora de ordem devido à natureza imprevisível das redes. Se outra requisição terminar após a última requisição disparada, o widget exibirá dados menos relevantes (ou potencialmente errados) do que se a última requisição disparada terminasse por último.

m.request() expõe seu objeto XMLHttpRequest subjacente através do parâmetro options.config, que permite que você salve uma referência a esse objeto e chame seu método abort quando necessário:

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;
}

Upload de arquivos ​

Para fazer upload de arquivos, primeiro você precisa obter uma referência a um objeto File. A maneira mais fácil de fazer isso é a partir de um <input type="file">.

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

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

O trecho acima renderiza um input de arquivo. Se um usuário escolher um arquivo, o evento onchange é acionado, que chama a função upload. e.target.files é uma lista de objetos File.

Em seguida, você precisa criar um objeto FormData para criar uma requisição multipart, que é uma requisição HTTP especialmente formatada que é capaz de enviar dados de arquivo no corpo da requisição.

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

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

Em seguida, você precisa chamar m.request e definir options.method para um método HTTP que usa body (por exemplo, POST, PUT, PATCH) e usar o objeto FormData como 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,
  });
}

Assumindo que o servidor esteja configurado para aceitar requisições multipart, as informações do arquivo serão associadas à key myfile.

Upload de múltiplos arquivos ​

É possível fazer upload de múltiplos arquivos em uma única requisição. Fazer isso tornará o upload em lote atômico, ou seja, nenhum arquivo será processado se houver um erro durante o upload, então não é possível ter apenas parte dos arquivos salvos. Se você quiser salvar o máximo de arquivos possível em caso de falha de rede, você deve considerar fazer upload de cada arquivo em uma requisição separada.

Para fazer upload de múltiplos arquivos, simplesmente adicione todos eles ao objeto FormData. Ao usar um input de arquivo, você pode obter uma lista de arquivos adicionando o atributo multiple ao 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,
  });
}

Monitorando o progresso ​

Às vezes, se uma requisição é inerentemente lenta (por exemplo, um upload de arquivo grande), é desejável exibir um indicador de progresso para o usuário para sinalizar que o aplicativo ainda está funcionando.

m.request() expõe seu objeto XMLHttpRequest subjacente através do parâmetro options.config, que permite que você anexe event listeners ao objeto 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(); // diz ao Mithril.js que os dados mudaram e um redesenho é necessário
      });
    },
  });
}

No exemplo acima, um input de arquivo é renderizado. Se o usuário escolher um arquivo, um upload é iniciado, e no callback config, um event handler é registrado. Este event handler é disparado sempre que houver uma atualização de progresso no XMLHttpRequest. Como o evento de progresso do XMLHttpRequest não é diretamente manipulado pelo motor de DOM virtual do Mithril.js, m.redraw() deve ser chamado para sinalizar ao Mithril.js que os dados foram alterados e um redesenho é necessário.

Convertendo a resposta para um tipo ​

Dependendo da arquitetura geral do aplicativo, pode ser desejável transformar os dados de resposta de uma requisição para uma classe ou tipo específico (por exemplo, para analisar uniformemente campos de data ou para ter métodos auxiliares).

Você pode passar um construtor como o parâmetro options.type e o Mithril.js o instanciará para cada objeto na resposta 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);
});

No exemplo acima, assumindo que /api/v1/users retorna um array de objetos, o construtor User será instanciado (ou seja, chamado como new User(data)) para cada objeto no array. Se a resposta retornasse um único objeto, esse objeto seria usado como o argumento body.

Respostas não-JSON ​

Às vezes, um endpoint do servidor não retorna uma resposta JSON: por exemplo, você pode estar requisitando um arquivo HTML, um arquivo SVG ou um arquivo CSV. Por padrão, o Mithril.js tenta analisar uma resposta como se fosse JSON. Para substituir esse comportamento, defina uma função options.deserialize personalizada:

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

No exemplo acima, a requisição recupera um arquivo SVG, não faz nada para analisá-lo (porque deserialize meramente retorna o valor como está), e então renderiza a string SVG como HTML confiável.

Claro, uma função deserialize pode ser mais elaborada:

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

function parseCSV(data) {
  // implementação ingênua para manter o exemplo simples
  return data.split('\n').map(function (row) {
    return row.split(',');
  });
}

Ignorando o fato de que a função parseCSV acima não lida com muitos casos que um parser CSV adequado lidaria, o código acima exibe um array de arrays.

Cabeçalhos (headers) personalizados também podem ser úteis a este respeito. Por exemplo, se você estiver requisitando um SVG, você provavelmente vai querer definir o tipo de conteúdo de acordo. Para substituir o tipo de requisição JSON padrão, defina options.headers para um objeto de pares key-value (chave-valor) correspondentes aos nomes e valores dos cabeçalhos de requisição.

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;
  },
});

Recuperando detalhes da resposta ​

Por padrão, o Mithril.js tenta analisar xhr.responseText como JSON e retorna o objeto analisado. Pode ser útil inspecionar uma resposta do servidor com mais detalhes e processá-la manualmente. Isso pode ser realizado passando uma função options.extract personalizada:

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);
});

O parâmetro para options.extract é o objeto XMLHttpRequest uma vez que sua operação é concluída, mas antes de ter sido passado para a cadeia de Promise retornada, então a Promise ainda pode acabar em um estado rejeitado se o processamento lançar uma exceção.

Emitindo fetches para endereços IP ​

Devido à forma (muito simplista) como os parâmetros são detectados em URLs, os segmentos de endereço IPv6 são confundidos como interpolações de parâmetros de caminho, e como os parâmetros de caminho precisam de algo para separá-los para serem interpolados corretamente, isso resulta em um erro sendo lançado.

javascript
// Isso não funciona
m.request('http://[2001:db8::990a:cd27:4d9e:79]:8080/some/path', {
  // ...
});

Para contornar isso, você deve passar o par endereço IPv6 + porta como um parâmetro.

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

Este não é um problema com endereços IPv4, e você pode usá-los normalmente.

javascript
// Isso funcionará como você espera
m.request('http://192.0.2.15:8080/some/path', {
  // ...
});

Por que JSON em vez de HTML ​

Muitos frameworks do lado do servidor fornecem um motor de view que interpola dados do banco de dados em um template antes de servir HTML (no carregamento da página ou via AJAX) e, em seguida, empregam jQuery para lidar com as interações do usuário.

Por outro lado, o Mithril.js é um framework projetado para aplicativos thick client, que normalmente baixam templates e dados separadamente e os combinam no navegador via JavaScript. Fazer o trabalho pesado de templating no navegador pode trazer benefícios como reduzir os custos operacionais, liberando recursos do servidor. Separar os templates dos dados também permite que o código do template seja armazenado em cache de forma mais eficaz e permite uma melhor reutilização do código em diferentes tipos de clientes (por exemplo, desktop, mobile). Outro benefício é que o Mithril.js permite um paradigma de desenvolvimento de UI retained mode (modo retido), o que simplifica muito o desenvolvimento e a manutenção de interações complexas do usuário.

Por padrão, m.request espera que os dados de resposta estejam no formato JSON. Em um aplicativo Mithril.js típico, esses dados JSON são então geralmente consumidos por uma view.

Você deve evitar tentar renderizar HTML dinâmico gerado pelo servidor com Mithril. Se você tiver um aplicativo existente que usa um sistema de templating do lado do servidor e deseja re-arquitetá-lo, primeiro decida se o esforço é viável para começar. Migrar de uma arquitetura de servidor thick para uma arquitetura de cliente thick é normalmente um esforço um tanto grande e envolve refatorar a lógica dos templates para serviços de dados lógicos (e os testes que acompanham).

Os serviços de dados podem ser organizados de muitas maneiras diferentes, dependendo da natureza do aplicativo. Arquiteturas RESTful são populares entre os provedores de API, e arquiteturas orientadas a serviços são frequentemente necessárias onde há muitos fluxos de trabalho altamente transacionais.

Por que XHR em vez de fetch ​

fetch() é uma API Web mais recente para buscar recursos de servidores, semelhante a XMLHttpRequest.

O m.request do Mithril.js usa XMLHttpRequest em vez de fetch() por vários motivos:

  • fetch ainda não está totalmente padronizado e pode estar sujeito a alterações de especificação.
  • As chamadas XMLHttpRequest podem ser abortadas antes de serem resolvidas (por exemplo, para evitar condições de corrida em UIs de pesquisa instantânea).
  • XMLHttpRequest fornece hooks para listeners de progresso para requisições de longa duração (por exemplo, uploads de arquivos).
  • XMLHttpRequest é suportado por todos os navegadores, enquanto fetch() não é suportado pelo Internet Explorer e Android mais antigos (anteriores ao 5.0 Lollipop).

Atualmente, devido à falta de suporte do navegador, fetch() normalmente requer um polyfill, que tem mais de 11kb não compactado - quase três vezes maior que o módulo XHR do Mithril.js.

Apesar de ser muito menor, o módulo XHR do Mithril.js suporta muitos recursos importantes e não tão triviais de implementar, como interpolação de URL e serialização de query string, além de sua capacidade de se integrar perfeitamente ao subsistema de redesenho automático do Mithril.js. O polyfill fetch não suporta nenhum desses e requer bibliotecas e boilerplates extras para atingir o mesmo nível de funcionalidade.

Além disso, o módulo XHR do Mithril.js é otimizado para endpoints baseados em JSON e torna esse caso mais comum apropriadamente conciso - ou seja, m.request(url) - enquanto fetch requer uma etapa explícita adicional para analisar os dados de resposta como JSON: fetch(url).then(function(response) {return response.json()})

A API fetch() tem algumas vantagens técnicas sobre XMLHttpRequest em alguns casos incomuns:

  • ele fornece uma API de streaming (no sentido de "vídeo streaming", não no sentido de programação reativa), o que permite melhor latência e consumo de memória para respostas muito grandes (ao custo de complexidade do código).
  • ele se integra à API Service Worker, que fornece uma camada extra de controle sobre como e quando as requisições de rede acontecem. Esta API também permite acesso a notificações push e recursos de sincronização em segundo plano.

Em cenários típicos, o streaming não fornecerá benefícios de desempenho perceptíveis porque geralmente não é aconselhável baixar megabytes de dados para começar. Além disso, os ganhos de memória da reutilização repetida de pequenos buffers podem ser compensados ou anulados se resultarem em repinturas excessivas do navegador. Por esses motivos, escolher o streaming fetch() em vez de m.request é recomendado apenas para aplicativos extremamente intensivos em recursos.

Evite anti-padrões ​

Promises não são os dados de resposta ​

O método m.request retorna uma Promise, não os próprios dados de resposta. Ele não pode retornar esses dados diretamente porque uma requisição HTTP pode levar muito tempo para ser concluída (devido à latência da rede) e, se o JavaScript esperasse por ela, congelaria o aplicativo até que os dados estivessem disponíveis.

javascript
// EVITE
var users = m.request('/api/v1/users');
console.log('list of users:', users);
// `users` NÃO é uma lista de usuários, é uma Promise

// PREFIRA
m.request('/api/v1/users').then(function (users) {
  console.log('list of users:', users);
});
Pager
Anteriorroute(root, defaultRoute, routes)
PróximoparseQueryString(string)

Distribuído sob a Licença MIT.

Copyright (c) 2024 Mithril Contributors

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

Distribuído sob a Licença MIT.

Copyright (c) 2024 Mithril Contributors