Axios: POST 请求在浏览器中有效,但在节点上无效

创建于 2017-07-20  ·  26评论  ·  资料来源: axios/axios

与 #318 类似,我无法在节点上使用 axios 发出 post 请求。 但在浏览器上,同一段代码似乎工作正常。

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

使用 unirest 它可以工作:

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);
});
  • axios 版本: v0.16.2
  • 环境: node v8.0.0,windows 8.1

最有用的评论

这可能被认为是#789 的重复。

我能够在 nodejs 中使用带有 Axios 的form-data包。 它基本上提供了一个类似于FormData的界面。 但是,您必须小心将它生成的头文件手动传递给 Axios。 例如:

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

所有26条评论

这可能被认为是#789 的重复。

我能够在 nodejs 中使用带有 Axios 的form-data包。 它基本上提供了一个类似于FormData的界面。 但是,您必须小心将它生成的头文件手动传递给 Axios。 例如:

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

是的,我尝试使用form-data包。 好吧,现在可以了,我将服务器端 API 更改为解析 JSON。

有人可以展示在 nodejs 应用程序中要做什么吗?

@ar412我试着在https://github.com/mzabriskie/axios/issues/1006#issuecomment -320165427 举个例子,你能澄清一下你在问什么吗?

@binki如果 axios 被用于在 restapi 发送带有一些数据的 post 请求,那么如何在 rest api(它在快速应用程序中)中检索该数据。

你可以试试这个https://expressjs.com/en/4x/api.html#req @ar412

@ar412要在 Express 中接收多部分数据,您可以使用busboybody-parser的文档所推荐的那样。 基本上,为了学习如何在 Express 中处理上传的文件,这与 Axios 根本没有关系,所以你最好在其他地方询问,因为你已经劫持了这个线程 ;-)。 例如,请参阅SO 上的此答案

收到错误消息:form.getHeaders() 不是函数

@宾基
嘿! 我有一个 hapi.js 服务器,我想在其中使用 fs.readFile(path) POST 存储在变量中的图像文件。
我无法将其作为 FormData() 发送

这是我的代码:

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

然后我将表单作为请求的正文发送并设置 options['Content-Type'] = 'multipart/form-data'

你能帮忙吗?
非常感激!

@bstolarz如果您传递Buffer ,您正在执行此操作,则不需要传递knownLength 。 此外,如果您阅读了您所写的内容(错误,从form-data的自述文件示例中复制并粘贴),您应该只提供filenamefilepath ——而不是两者。 唯一可能对您有意义的设置是:

  1. filenamefilepath
  2. contentType (如果服务器需要特定的 Content-Type 值,否则可能可以省略)。

我敢打赌,您发送的图像的大小不是19806因为19806值是您从form-data的示例中复制的form-data本身抛出错误或在 HTTP 请求中写入无效数据。 您是否检查过axios.post()返回的Promise是否被拒绝或是否抛出异常? 如果 axios 请求被拒绝,检查错误对象是否有result键,如果有,检查err.result.status值是什么。 如果它是 404 或 403 以外的 4xx 值,则可能意味着服务器拒绝了可能由不正确的已知大小导致的格式错误的请求。

@宾基
嘿,谢谢你的回复。 我更正了你提到的事情,所以现在我的代码是

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'

          })

但是服务器抛出 411“需要长度”(我也尝试过 fs.createReadStream ,它似乎完全受 FormData 支持,但我得到了相同的长度错误)。

@宾基
我设法在最低层中设置了标题内容长度,并且不再出现该错误。
现在我没有从服务器得到 411,我又回到了我曾经有过的错误 400-“没有提供文件”。

这就是请求的样子

开始请求

{ 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' }

希望你检查一下。 对您来说,这看起来是一个合理的要求吗?

提前致谢

@bstolarz我已尽力重现您的问题。 然而,我不能。 我写了这段代码来尝试模仿你在做什么。 但是,我故意省略了手动设置Content-Length 。 我相信您设置Content-Length的事实可能与您的问题有关。 请尝试让form-data为您计算它——这就是您必须调用form.getHeaders()

请参阅此贴: https : axios.post()axios()来匹配我的例子,它对你有用吗?

如果您仍然有问题,请尝试将您的代码移动到它自己的脚本中并在那里重现它。 它可以帮助您解决问题。 如果你仍然被卡住,请发布完整的代码,包括调用axios()axios.post()作为要点并将其链接到这里,如果可以,我会研究它。

它仍然根本不起作用......

@rodrigogs如果你需要帮助,你需要更详细;-)。

我对 Axios 进行了这项有益的研究,最终解决方案对我有用(链接

这是它的复制/粘贴:

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 ,我也遇到同样的错误。 @binki ,有什么想法吗?
错误
form.getHeaders 不是函数

@smplyjr你能提供更多的背景信息,让我知道你是如何得到form吗? 没有代码,我们无法判断您在做什么或帮助您。

对于nodejs用户,使用querystring lib解决,如下:

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

正如@heldrida提到的,使用查询字符串。 这就是 axios 建议在此处执行此操作的方式: https ://www.npmjs.com/package/axios#nodejs form-data包在这里有各种各样的问题,你最终只能拔出你的头发试图让它工作。

@heldrida @ashok-sc 如何使用querystringqs上传带有 axios 的文件? 我正在使用 axios 从 AWS Lambda 上传文件,但显然我无法从那里访问File对象

@bstolarz我已尽力重现您的问题。 然而,我不能。 我写了这段代码来尝试模仿你在做什么。 但是,我故意省略了手动设置Content-Length 。 我相信您设置Content-Length的事实可能与您的问题有关。 请尝试让form-data为您计算它——这就是您必须调用form.getHeaders()

请参阅此贴: https : axios.post()axios()来匹配我的例子,它对你有用吗?

如果您仍然有问题,请尝试将您的代码移动到它自己的脚本中并在那里重现它。 它可以帮助您解决问题。 如果你仍然被卡住,请发布完整的代码,包括调用axios()axios.post()作为要点并将其链接到这里,如果可以,我会研究它。

谢啦。 添加 Content-Length 解决了我的问题,现在我可以使用带有 FormData 的 axios backend-2-backend 调用:
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; }] };

这可能被认为是#789 的重复。

我能够在 nodejs 中使用带有 Axios 的form-data包。 它基本上提供了一个类似于FormData的界面。 但是,您必须小心将它生成的头文件手动传递给 Axios。 例如:

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

这里的另一种方式。 通过这种方式,您可以将代理添加到和其他配置中:

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

这对我有用。

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

感谢这篇文章的所有贡献者。 在花费数小时使用 Axios 将我的form-data表单发布到将其发布到 Amazon 存储桶的后端遇到问题后,结果证明该解决方案是手动设置content-length ....

对于其他有问题的人,我的代码最终就像这样,也许它可以帮助下一个遇到问题的人来解决这个问题:)

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

在花费数小时使用 Axios 将我的form-data表单发布到将其发布到 Amazon 存储桶的后端遇到问题后,结果证明该解决方案是手动设置content-length ....

是的。 我和我的一位同事在尝试将文件 POST 到后端时也花了几个小时,后端会回复没有提交数据,而这显然是,因为我们可以跟踪请求并查看内容。 问题是缺少内容长度标头。

请注意,如果您要向 FormData 添加缓冲区,则调用formData.getLengthSync()但是如果您正在处理流,则必须首先使用fs.statSync(filePath).size对文件进行统计或以另一种方式获取完整大小(例如从上游的内容长度标头),例如,如果流来自请求或套接字或其他任何内容。 如果您的流来自磁盘,则fs.statSync(filePath).size将以字节为单位给出大小,因此您可以在附加到 FormData 时将其添加到 knownLength 中:

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

当然,所有 Sync 方法都可以切换为异步方法和 await 关键字。

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

我猜你确实在 formData 之前忘记了await ......

此页面是否有帮助?
0 / 5 - 0 等级