Axios: La solicitud POST funciona en el navegador pero no en el nodo

Creado en 20 jul. 2017  ·  26Comentarios  ·  Fuente: axios/axios

Similar al # 318, no puedo hacer una solicitud de publicación con axios en el nodo. Pero en el navegador, el mismo código parece funcionar bien.

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

Con disturbios 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);
});
  • versión de axios :
  • Entorno: nodo v8.0.0, windows 8.1

Comentario más útil

Esto podría considerarse un duplicado de # 789.

Pude usar el paquete form-data con Axios en nodejs. Básicamente proporciona una interfaz similar a FormData . Sin embargo, debe tener cuidado de pasar los encabezados que genera a Axios manualmente. Por ejemplo:

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 comentarios

Esto podría considerarse un duplicado de # 789.

Pude usar el paquete form-data con Axios en nodejs. Básicamente proporciona una interfaz similar a FormData . Sin embargo, debe tener cuidado de pasar los encabezados que genera a Axios manualmente. Por ejemplo:

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

Sí, lo intenté con ese paquete form-data . Bueno, ahora está bien, cambié la API del lado del servidor para analizar JSON en su lugar.

¿Alguien puede mostrar qué se debe hacer en la aplicación nodejs?

@ ar412 Traté de dar un ejemplo en https://github.com/mzabriskie/axios/issues/1006#issuecomment -320165427, ¿podría aclarar lo que está preguntando?

@binki si axios se está utilizando para enviar una solicitud de publicación en un restapi con algunos datos, entonces cómo recuperar esos datos en el resto de la API (que se encuentra dentro de una aplicación rápida).

puedes probar esto https://expressjs.com/en/4x/api.html#req @ ar412

@ ar412 Para recibir datos de busboy como lo recomiendan los documentos de body-parser . Básicamente, para aprender a manejar archivos cargados en Express, eso no está relacionado con Axios en absoluto, por lo que es mejor que pregunte en otro lugar, ya que ha secuestrado este hilo ;-). Por ejemplo, vea esta respuesta en SO .

Recibí un mensaje de error: form.getHeaders () no es una función

@binki
¡Oye! Tengo un servidor hapi.js en el que quiero PUBLICAR un archivo de imagen almacenado en una variable usando fs.readFile (ruta).
No puedo hacer que funcione enviando eso como FormData ()

Este es mi codigo:

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

Luego envío el formulario como el cuerpo de la solicitud y configuro las opciones ['Content-Type'] = 'multipart / form-data'

¿Podrías ayudarme?
¡Muy apreciado!

@bstolarz si pasa un Buffer , que está haciendo, no necesita pasar knownLength . Además, si lee lo que escribió (err, copió y pegó de los ejemplos README de filename o filepath no ambos. Las únicas cosas que podría tener sentido que establezca son:

  1. filename O filepath
  2. contentType (en caso de que el servidor requiera un valor de tipo de contenido particular, de lo contrario probablemente se puede omitir).

Lo que apuesto a que está sucediendo es que el tamaño de la imagen que está enviando tiene un tamaño que no es 19806 porque ese 19806 es algo que copió de un ejemplo en form-data documentos en lugar de calcularse para sus propios datos. Es probable que esto haga que form-data arroje un error o escriba datos no válidos en la solicitud HTTP. ¿Verificó si el axios.post() devuelto Promise es rechazado o si se lanza una excepción? Si se rechaza la solicitud de axios, verifique si el objeto de error tiene una clave result y, si la tiene, verifique cuál es el err.result.status . Si es un valor 4xx distinto de 404 o 403, eso probablemente significa que el servidor está rechazando una solicitud mal formada que podría resultar de un tamaño conocido incorrecto.

@binki
Oye, gracias por tu respuesta. Corregí las cosas que mencionaste, así que ahora mi código es

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'

          })

Pero el servidor arroja 411 "Se requiere longitud" (también probé fs.createReadStream que parece ser totalmente compatible con FormData, pero obtengo el mismo error de longitud).

@binki
Logré establecer la longitud del contenido del encabezado en la capa más baja y ya no recibo ese error.
Ahora que no obtengo un 411 del servidor, volví al error que una vez tuve 400- "No se proporcionan archivos".

Así es como se ve la solicitud

Solicitud de inicio

{ 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 veas esto. Para usted, ¿le parece una solicitud razonable?

Gracias por adelantado

@bstolarz He hecho todo lo posible para reproducir su problema. Sin embargo, no puedo. Escribí este código para intentar imitar lo que estás haciendo. Sin embargo, omití a propósito la configuración manual Content-Length . Estoy convencido de que el hecho de que esté configurando Content-Length podría estar relacionado con su problema. Por favor, intente dejar que form-data se encargue de calcularlo por usted; es por eso que debe llamar a form.getHeaders() .

Vea esta pasta: https://gist.github.com/binki/10ac3e91851b524546f8279733cdadad . ¿Quizás pueda modificar la forma en que llama axios.post() o axios() para que coincida con mi ejemplo y funcionará para usted?

Si aún tiene problemas, intente mover su código a su propio script y reproducirlo allí. Puede ayudarlo a resolver el problema. Si todavía está atascado, publique el código completo, incluida la llamada a axios() o axios.post() como resumen y

Todavía no funciona en absoluto ...

@rodrigogs Si quieres ayuda, necesitarás ser más detallado ;-).

Seguí esta útil investigación sobre Axios y la solución final funciona para mí ( enlace )

Aquí está la copia / pegado del mismo:

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 , también obtengo el mismo error. @binki , ¿alguna idea?
El error
form.getHeaders no es una función

@smplyjr ¿Puedes dar más contexto y decirme cómo obtienes form ? Sin código, no podemos decirle lo que está haciendo ni ayudarlo.

Para los usuarios de nodejs, resuelva usando la librería querystring, de la siguiente manera:

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

Como mencionó @heldrida , use querystring. Esa es la forma en que axios recomienda hacerlo también aquí: https://www.npmjs.com/package/axios#nodejs. El paquete form-data tiene todo tipo de problemas aquí y terminarás tirándote de los pelos tratando de que funcione.

@heldrida @ querystring o qs para cargar un archivo con axios? Estoy usando axios para cargar archivos desde AWS Lambda y obviamente no tengo acceso al objeto File desde allí

@bstolarz He hecho todo lo posible para reproducir su problema. Sin embargo, no puedo. Escribí este código para intentar imitar lo que estás haciendo. Sin embargo, omití a propósito la configuración manual Content-Length . Estoy convencido de que el hecho de que esté configurando Content-Length podría estar relacionado con su problema. Por favor, intente dejar que form-data se encargue de calcularlo por usted; es por eso que debe llamar a form.getHeaders() .

Vea esta pasta: https://gist.github.com/binki/10ac3e91851b524546f8279733cdadad . ¿Quizás pueda modificar la forma en que llama axios.post() o axios() para que coincida con mi ejemplo y funcionará para usted?

Si aún tiene problemas, intente mover su código a su propio script y reproducirlo allí. Puede ayudarlo a resolver el problema. Si todavía está atascado, publique el código completo, incluida la llamada a axios() o axios.post() como resumen y

Gracias hombre. Agregar Content-Length resolvió mi problema ahora puedo usar llamadas axios backend-2-backend con 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; }] };

Esto podría considerarse un duplicado de # 789.

Pude usar el paquete form-data con Axios en nodejs. Básicamente proporciona una interfaz similar a FormData . Sin embargo, debe tener cuidado de pasar los encabezados que genera a Axios manualmente. Por ejemplo:

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

Otra forma aquí. De esta manera, puede agregar un agente proxy y otras configuraciones:

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

Esto funciona para mi.

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

Gracias a todos los colaboradores de esta publicación. Después de pasar horas de tener problemas POSTANDO mi formulario form-data usando Axios, en un backend que lo publica en un bucket de Amazon, la solución resultó para configurar manualmente content-length ....

Para cualquier otra persona que tenga problemas, mi código terminó como, tal vez pueda ayudar al siguiente en la línea que tiene problemas para que esto funcione :)

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

Después de pasar horas de tener problemas POSTANDO mi formulario form-data usando Axios, en un backend que lo publica en un bucket de Amazon, la solución resultó para configurar manualmente content-length ....

Si. Un colega y yo también pasamos varias horas tratando de enviar archivos POST a un backend que respondería que no se estaban enviando datos, mientras que obviamente sí, ya que podíamos rastrear la solicitud y ver el contenido. El problema era que faltaba el encabezado de longitud del contenido.

Como nota, si está agregando un búfer a FormData, está bien si llama a formData.getLengthSync() pero si está tratando con una transmisión, primero debe registrar el archivo con fs.statSync(filePath).size u obtenga el tamaño completo de otra manera (como desde un encabezado de longitud de contenido desde un flujo ascendente), por ejemplo, si el flujo proviene de una solicitud o socket o lo que sea. Si su transmisión proviene del disco, fs.statSync(filePath).size le dará el tamaño en bytes, por lo que puede agregarlo en knownLength al agregar a FormData:

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

Por supuesto, todos los métodos de sincronización se pueden cambiar por los asíncronos y la palabra clave await.

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

Supongo que olvidaste un await antes de formData de hecho ...

¿Fue útil esta página
0 / 5 - 0 calificaciones