Axios: A solicitação POST funciona no navegador, mas não no nó

Criado em 20 jul. 2017  ·  26Comentários  ·  Fonte: axios/axios

Semelhante ao # 318, não consigo fazer uma solicitação de postagem com axios no nó. Mas no navegador, o mesmo código parece funcionar bem.

const fdata = new FormData();
fdata.append('user', u);
fdata.append('hostnames', n.join(' '));
const host = localStorage.getItem('host');
const port = localStorage.getItem('port');
axios({
  url: `http://${host}:${port}/hosts/remove`,
  method: 'post',
  data: fdata
}).then(response => {
  if (response.status === 200) {
    console.log(response.data);
    console.log('Removed host successfully');
  }
  return null;
}).catch(er => console.log(er));

Com o unirest funciona:

unirest.post(`http://${host}:${port}/hosts/remove`)
.headers({ 'Content-Type': 'multipart/form-data' })
.field('user', u)
.field('hostnames', h.join(' '))
.end(response => {
  console.log(response.body);
});
  • versão axios :
  • Ambiente: nó v8.0.0, windows 8.1

Comentários muito úteis

Isso pode ser considerado uma duplicata de # 789.

Consegui usar o pacote form-data com Axios em nodejs. Ele basicamente fornece uma interface semelhante a FormData . No entanto, você deve ter cuidado para passar os cabeçalhos que ele gera para o Axios manualmente. Por exemplo:

const axios = require('axios');
const FormData = require('form-data');

const form = new FormData();
// Second argument  can take Buffer or Stream (lazily read during the request) too.
// Third argument is filename if you want to simulate a file upload. Otherwise omit.
form.append('field', 'a,b,c', 'blah.csv');
axios.post('http://example.org/endpoint', form, {
  headers: form.getHeaders(),
}).then(result => {
  // Handle result…
  console.log(result.data);
});

Todos 26 comentários

Isso pode ser considerado uma duplicata de # 789.

Consegui usar o pacote form-data com Axios em nodejs. Ele basicamente fornece uma interface semelhante a FormData . No entanto, você deve ter cuidado para passar os cabeçalhos que ele gera para o Axios manualmente. Por exemplo:

const axios = require('axios');
const FormData = require('form-data');

const form = new FormData();
// Second argument  can take Buffer or Stream (lazily read during the request) too.
// Third argument is filename if you want to simulate a file upload. Otherwise omit.
form.append('field', 'a,b,c', 'blah.csv');
axios.post('http://example.org/endpoint', form, {
  headers: form.getHeaders(),
}).then(result => {
  // Handle result…
  console.log(result.data);
});

Sim, tentei com aquele pacote form-data . Bem, está tudo bem agora, mudei a API do lado do servidor para analisar JSON em vez disso.

alguém pode mostrar o que deve ser feito no app nodejs ??

@ ar412 Tentei dar um exemplo em https://github.com/mzabriskie/axios/issues/1006#issuecomment -320165427, você poderia esclarecer o que está perguntando?

@binki se axios está sendo usado para enviar uma solicitação de postagem em um restapi com alguns dados, como recuperar esses dados na api restante (que está dentro de um aplicativo expresso).

você pode tentar https://expressjs.com/en/4x/api.html#req @ ar412

@ ar412 Para receber dados multiparte no Express, você pode usar algo como busboy conforme recomendado pelos documentos de body-parser . Basicamente, para aprender como lidar com arquivos carregados no Express, isso não está relacionado ao Axios, então é melhor você perguntar em outro lugar já que você sequestrou este tópico ;-). Por exemplo, veja esta resposta em SO .

Mensagem de erro recebida: form.getHeaders () não é uma função

@binki
Ei! Eu tenho um servidor hapi.js no qual desejo POSTAR um arquivo de imagem armazenado em uma variável usando fs.readFile (caminho).
Não consigo fazer funcionar enviando isso como FormData ()

Este é o meu código:

fs.readFile(__dirname+'/../static/lisa_server.jpg', (error, imageData) => {
          var form = new FormData()

            form.append('file', imageData,  {
            filename: 'unicycle.jpg', // ... or:
            filepath: '/../static/lisa_server.jpg',
            contentType: 'image/jpg',
            knownLength: 19806
          })

Em seguida, envio o formulário como o corpo da solicitação e defino as opções ['Content-Type'] = 'multipart / form-data'

Você poderia ajudar?
Muito apreciado!

@bstolarz se você passar Buffer , o que está fazendo, você não precisa passar knownLength . Além disso, se você leu o que escreveu (errar, copiou e colou dos exemplos do README de filename ou filepath - não ambos. As únicas coisas que pode fazer sentido para você definir são:

  1. filename OR filepath
  2. contentType (caso o servidor exija um determinado valor de Content-Type, caso contrário, provavelmente pode ser omitido).

O que aposta está acontecendo é que o tamanho da imagem que você está enviando tenha um tamanho que não é 19806 causa que 19806 valor é algo que você copiou de um exemplo em form-data documentos de, em vez de calculados com seus próprios dados. Isso provavelmente fará com que form-data emita um erro ou grave dados inválidos na solicitação HTTP. Você verificou se o axios.post() retornado Promise está rejeitando ou se uma exceção é lançada? Se a solicitação de axios for rejeitada, verifique se o objeto de erro possui uma chave result e, se tiver, verifique qual é o err.result.status . Se for um valor 4xx diferente de 404 ou 403, isso provavelmente significa que o servidor está rejeitando uma solicitação malformada que pode resultar de um tamanho conhecido incorreto.

@binki
Ei, obrigado pela sua resposta. Corrigi o que você mencionou, então agora meu código é

fs.readFile(__dirname+'/../static/lisa_server.jpg', (error, imageData) => {
          var form = new FormData()

          form.append('file', imageData, {
               filepath: __dirname+'/../static/lisa_server.jpg',
               contentType: 'image/jpg'

          })

Mas o servidor exibe 411 "Comprimento é necessário" (também tentei fs.createReadStream, que parece ser totalmente compatível com FormData, mas recebo o mesmo erro de comprimento).

@binki
Eu consegui definir o comprimento do conteúdo do cabeçalho na camada mais baixa e não recebo mais esse erro.
Agora que não recebo um 411 do servidor, voltei ao erro que uma vez tinha 400 - "Nenhum arquivo fornecido".

É assim que o pedido se parece

Pedido inicial

{ adapter: [Function: httpAdapter],
  transformRequest: { '0': [Function: transformRequest] },
  transformResponse: { '0': [Function: transformResponse] },
  timeout: 5000,
  xsrfCookieName: 'XSRF-TOKEN',
  xsrfHeaderName: 'X-XSRF-TOKEN',
  maxContentLength: -1,
  validateStatus: [Function: validateStatus],
  headers:
   { common: { Accept: 'application/json, text/plain, */*' },
     delete: {},
     get: {},
     head: {},
     post: { 'Content-Type': 'application/json' },
     put: { 'Content-Type': 'application/json' },
     patch: { 'Content-Type': 'application/json' },
     'User-Agent': 'trojan server 1.0',
     'X-Origin-Panamera': 'Staging',
     'Content-Length': 25247 },
  baseURL: 'https://letgoar-a.akamaihd.net/api/v1',
  method: 'post',
  url: 'https://<baseDomain>/api/v1/images',
  data:
   FormData {
     _overheadLength: 218,
     _valueLength: 25247,
     _valuesToMeasure: [],
     writable: false,
     readable: true,
     dataSize: 0,
     maxDataSize: 2097152,
     pauseStreams: true,
     _released: false,
     _streams:
      [ '----------------------------677738213014296377492349\r\nContent-Disposition: form-data; name="file"; filename="/Users/brenda/repos/qreator2/qreator/trojan-server/src/static/lisa_server.jpg"\r\nContent-Type: image/jpg\r\n\r\n',
        <Buffer ff d8 ff e0 00 10 4a 46 49 46 00 01 01 01 00 48 00 48 00 00 ff e2 11 2c 49 43 43 5f 50 52 4f 46 49 4c 45 00 01 01 00 00 11 1c 61 70 70 6c 02 00 00 00 ... >,
        [Function: bound ] ],
     _currentStream: null,
     _boundary: '--------------------------677738213014296377492349' },
  'Content-Type': 'multipart/form-data' }

Espero que você dê uma olhada. Para você, isso parece um pedido razoável?

desde já, obrigado

@bstolarz Fiz o meu melhor para reproduzir o seu problema. No entanto, não posso. Escrevi este código para tentar imitar o que você está fazendo. No entanto, omiti propositalmente a configuração manual de Content-Length . Estou convencido de que o fato de você estar definindo Content-Length pode estar relacionado ao seu problema. Por favor, tente deixar form-data fazer o cálculo para você - é por isso que você deve chamar form.getHeaders() .

Veja esta pasta: https://gist.github.com/binki/10ac3e91851b524546f8279733cdadad . Talvez você possa modificar a maneira como você chama axios.post() ou axios() para coincidir com o meu exemplo e funcionará para você?

Se você ainda tiver problemas, tente mover seu código para seu próprio script e reproduzi-lo lá. Isso pode ajudá-lo a resolver o problema. Se você ainda estiver travado, poste o código completo, incluindo a chamada para axios() ou axios.post() como um resumo e vincule-o aqui e, se eu puder, irei analisá-lo.

Ainda não funciona ...

@rodrigogs Se você quiser ajuda, você precisará ser mais detalhado ;-).

Acompanhei essa pesquisa útil no Axios e a solução final funciona para mim ( link )

Aqui está o copiar / colar dele:

import fs from 'fs';
import FormData from 'form-data';
import axios from 'axios';

let data = fs.createReadStream(__dirname + '/test.jpg');
let form = new FormData();

form.append('type','image');
form.append('media',data,'test.jpg');

function getHeaders(form) {
    return Promise((resolve, reject) => {
        form.getLength((err, length) => {
            if(err) { reject(err); }
            let headers = Object.assign({'Content-Length': length}, form.getHeaders());
            resolve(headers);
         });
    });
}

getHeaders(form)
.then((headers) => {
    return axios.post(url, form, {headers:headers})
})
.then((response)=>{
    console.log(response.data)
})
.catch(e=>{console.log(e)})

@westofpluto , também recebo o mesmo erro. @binki , alguma
O erro
form.getHeaders não é uma função

@smplyjr Você pode fornecer mais contexto e me dizer como você conseguiu form ? Sem código, não podemos dizer o que você está fazendo ou ajudá-lo.

Para os usuários do nodejs, resolva usando a lib querystring, da seguinte maneira:

const querystring = require('querystring')
axios
  .post(URL, querystring.stringify(data))
  .then((response) => ...)
  .catch((error) => ...)

Como @heldrida mencionou, use querystring. É assim que a axios também recomenda fazer aqui: https://www.npmjs.com/package/axios#nodejs. O pacote form-data tem todos os tipos de problemas aqui e você vai acabar arrancando os cabelos tentando fazer funcionar.

@heldrida @ ashok-sc Como preciso usar querystring ou qs para carregar um arquivo com axios? Estou usando axios para fazer upload de arquivos de um AWS Lambda e obviamente não tenho acesso ao objeto File de lá

@bstolarz Fiz o meu melhor para reproduzir o seu problema. No entanto, não posso. Escrevi este código para tentar imitar o que você está fazendo. No entanto, omiti propositalmente a configuração manual de Content-Length . Estou convencido de que o fato de você estar definindo Content-Length pode estar relacionado ao seu problema. Por favor, tente deixar form-data fazer o cálculo para você - é por isso que você deve chamar form.getHeaders() .

Veja esta pasta: https://gist.github.com/binki/10ac3e91851b524546f8279733cdadad . Talvez você possa modificar a maneira como você chama axios.post() ou axios() para coincidir com o meu exemplo e funcionará para você?

Se você ainda tiver problemas, tente mover seu código para seu próprio script e reproduzi-lo lá. Isso pode ajudá-lo a resolver o problema. Se você ainda estiver travado, poste o código completo, incluindo a chamada para axios() ou axios.post() como um resumo e vincule-o aqui e, se eu puder, irei analisá-lo.

Obrigado cara. Adicionar Content-Length resolveu meu problema agora, posso usar chamadas axios backend-2-backend com FormData:
const options = { method: 'POST', url: myUrl, data: justJsonBody, transformRequest: [function (data, headers) { const formData = convertToFormData(data); // returrns ForrmData from form-data headers['Content-Type'] = formData.getHeaders()['content-type']; headers['Content-Length'] = formData._overheadLength; return formData; }] };

Isso pode ser considerado uma duplicata de # 789.

Consegui usar o pacote form-data com Axios em nodejs. Ele basicamente fornece uma interface semelhante a FormData . No entanto, você deve ter cuidado para passar os cabeçalhos que ele gera para o Axios manualmente. Por exemplo:

const axios = require('axios');
const FormData = require('form-data');

const form = new FormData();
// Second argument  can take Buffer or Stream (lazily read during the request) too.
// Third argument is filename if you want to simulate a file upload. Otherwise omit.
form.append('field', 'a,b,c', 'blah.csv');
axios.post('http://example.org/endpoint', form, {
  headers: form.getHeaders(),
}).then(result => {
  // Handle result…
  console.log(result.data);
});

Outra maneira aqui. Desta forma, você pode adicionar agente proxy e outras configurações:

const axios = require('axios');
const FormData = require('form-data');
const ProxyAgent = require('proxy-agent');

const form = new FormData();
// Second argument  can take Buffer or Stream (lazily read during the request) too.
// Third argument is filename if you want to simulate a file upload. Otherwise omit.
form.append('field', 'a,b,c', 'blah.csv');
axios({
  method: 'POST',
  url: 'http://example.org/endpoint',
  data: form,
  agent: new ProxyAgent("https://username:[email protected]:8080"),
  headers: bodyFormData.getHeaders()
}).then(result => {
  // Handle result…
  console.log(result.data);
});

Isso funciona para mim.

// ES6
import axios from 'axios';
import FormData from 'form-data';
import fs from 'fs';

FormData.prototype.getHeadersWithContentLength = function getHeadersWithContentLength() {
  return new Promise((resolve, reject) => {
    this.getLength((err, length) =>
      err ? reject(err) : resolve({ ...this.getHeaders(), 'Content-Length': length })
    )
  })
}

const payload = new FormData();
const form = new formidable.IncomingForm();

form.parse(req, (err, fields, { file }) => {
  if (err) return;

  payload.append("file", fs.createReadStream(file.path), {
    filename: file.name,
    contentType: file.type
  });

  payload.getHeadersWithContentLength().then(headers => {
    api
      .post(endpoint, payload, { headers })
      .then(({ data }) => data)
      .then(data => res.json({ data }));
  });
});

Obrigado a todos os contribuidores deste post. Depois de passar horas tendo problemas para POSTAR meu formulário form-data usando Axios, para um back-end que o publica em um bucket da Amazon, a solução acabou sendo definir manualmente content-length ....

Para qualquer outra pessoa com problemas, meu código acabou, talvez possa ajudar o próximo na fila tendo problemas para fazer isso funcionar :)

const axios = require('axios');
const FormData = require('form-data');

// Where buffer is a file
formData.append('file', buffer);

// Added a promise version like seen in earlier comments to get this
const contentLength = await formData.getLength();

await axios(`<ENDPOINT>`, {
    method: 'POST',
    baseURL: <BASE_URL>,
    params: {
        fileName: '<FILE_NAME>.png'
    },
    headers: {
        authorization: `Bearer <TOKEN>`,
        ...formData.getHeaders(),
        'content-length': contentLength
    },
    data: formData
});

Depois de passar horas tendo problemas para POSTAR meu formulário form-data usando Axios, para um back-end que o publica em um bucket da Amazon, a solução acabou sendo definir manualmente content-length ....

sim. Eu e um colega meu também passamos várias horas tentando POSTAR arquivos em um back-end que responderia que nenhum dado estava sendo enviado, embora obviamente estivesse, já que podíamos rastrear a solicitação e ver o conteúdo. O problema era a falta do cabeçalho do comprimento do conteúdo.

Como uma observação, se você estiver adicionando um buffer ao FormData, não há problema se você chamar formData.getLengthSync() mas se estiver lidando com um fluxo, você deve primeiro registrar o arquivo com fs.statSync(filePath).size ou obter o tamanho total de outra maneira (como de um cabeçalho de comprimento de conteúdo de um upstream), por exemplo, se o fluxo vem de uma solicitação ou soquete ou qualquer outra coisa. Se seu stream vier do disco, fs.statSync(filePath).size dará o tamanho em bytes, então você pode adicioná-lo em knownLength ao anexar a FormData:

formData.append("file", fs.createReadStream(filePath), { filename: 'whatever.pdf', knownLength: fs.statSync(filePath).size });

É claro que todos os métodos de sincronização podem ser trocados para os assíncronos e a palavra-chave await.

// Added a promise version like seen in earlier comments to get this
const contentLength = formData.getLength();

Acho que você esqueceu um await antes de formData, de fato ...

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

emaincourt picture emaincourt  ·  3Comentários

Shu-Ji picture Shu-Ji  ·  3Comentários

shaosh picture shaosh  ·  3Comentários

reggi picture reggi  ·  3Comentários

ghprod picture ghprod  ·  3Comentários