javascript - axios onUploadProgress 和 onDownloadProgress 不适用于 CORS

标签 javascript node.js cors xmlhttprequest axios

我有一个用 Node.js 编写的服务器,以及一个在浏览器中运行的 Web 客户端。客户端应从服务器上传和下载一些文件。服务器不是原来交付客户端的那个,所以我们这里就出现了跨域的情况。

服务器使用 cors实现跨域请求的中间件。由于我还使用了一些自定义 header ,因此我将其与如下配置一起使用:

api.use(cors({
  origin: '*',
  allowedHeaders: [ 'content-type', 'authorization', 'x-metadata', 'x-to' ],
  exposedHeaders: [ 'content-type', 'content-disposition', 'x-metadata' ]
}));

相比之下,客户端使用的是axios ,上传文件的代码如下所示:

await axios({
  method: 'post',
  url: 'https://localhost:3000/api/v1/add-file',
  data: content,
  headers: {
    // ...
  }
});

到目前为止,一切都按预期进行,尤其是在 CORS 方面。

现在我想跟踪上传的进度,因此我将 onUploadProgress 回调添加到 axios 调用中,如 suggested by its documentation :

await axios({
  method: 'post',
  url: 'https://localhost:3000/api/v1/add-file',
  data: content,
  headers: {
    // ...
  },
  onUploadProgress (progressEvent) {
    console.log({ progressEvent });
  }
});

如果我现在运行客户端,上传仍然有效——但永远不会调用 onUploadProgress 回调。我读到并不是所有的浏览器都支持这个(在内部这只是绑定(bind)到底层 XmlHttpRequest 对象的 onprogress 事件),但最新的 Chrome 应该能够做到这一点(并且如果我尝试不同的设置,其中不涉及 CORS,一切似乎都有效)。所以我认为这是一个 CORS 问题。

我有read一旦您将监听器添加到 onprogress 事件,就会发送预检请求。这反过来意味着服务器必须接受 OPTIONS 请求。为此,我在服务器上添加了以下代码,上述对 api.use 的调用之前:

api.options('*', cors({
  origin: '*',
  methods: [ 'GET', 'POST' ],
  allowedHeaders: [ 'content-type', 'authorization', 'x-metadata', 'x-to' ],
  exposedHeaders: [ 'content-type', 'content-disposition', 'x-metadata' ],
  optionsSuccessStatus: 200
}));

结果:上传仍然有效,但仍然没有引发 onUploadProgress 事件。所以我假设我遗漏了一些与 CORS 相关的东西。问题只是:我错过了什么?

我也曾尝试使用更大的文件(将近 2 GB,而不是几 KB)重新运行相同的设置,但结果是一样的。有谁知道我在这里做错了什么?

更新

回复一些评论:首先,我的axios版本是0.18.0,所以我使用的是可用的最新稳定版本。

服务器的源代码可以在存储库中找到 thenativeweb/wolkenkit-depot . CORS部分配置here .请注意,这与我上面发布的版本略有不同,因为我做了一些本地更改。然而,这是你需要处理的地方。

要构建服务器,您需要安装 Node.js 和 Docker。然后,运行:

$ npm install
$ npx roboter build

这将生成一个新的 Docker 镜像,其中包含服务器的代码(您需要此 Docker 镜像才能运行下面的客户端测试)。请注意,如果您在服务器上更改任何内容,则必须重新构建此 Docker 镜像。

客户端可以在存储库中找到 thenativeweb/wolkenkit-depot-client-js , 在分行 issue-145-notifiy-about-progress-when-uploading-or-downloading-files .

在文件中DepotClient.js我在 onUploadProgress 中添加了一个事件监听器,它应该只是抛出一个异常(这反过来应该非常明显地表明事件已引发)。

此代码随后由 this test 运行(对此有更多测试,但这是其中之一),这应该会导致异常,但事实并非如此。

要再次运行测试,您必须安装 Node.js 和 Docker,并且必须按照之前的描述构建服务器。然后使用以下命令代表您运行测试:

$ npm install
$ npx roboter test --type integration

我的期望是至少有一个 addFile 测试应该归档。事实上,它们都变成了绿色,这意味着上传本身可以完美运行,但永远不会引发 onUploadProgress 事件。以防万一你想知道执行测试的环境:我在这里使用 puppeteer,它是一个 headless 的 Chrome。

最佳答案

由于该代码运行良好(无论是否使用 cors),我假设您使用的是旧的 axios 版本。 onUploadProgress添加到版本 0.14.0 .

如果您使用的是旧版本,您可以使用:progress而不是 onUploadProgress ,或者只更新到最新版本。

0.14.0(2016 年 8 月 27 日)

BREAKING Splitting progress event handlers into onUploadProgress and onDownloadProgress (#423)

await axios({
  method: 'post',
  url: 'https://localhost:3000/api/v1/add-file',
  data: content,
  headers: {
    // ...
  },
  // Old version
  progress (progressEvent) {
    console.log({ progressEvent });
  },
  // New version
  onUploadProgress (progressEvent) {
    console.log({ progressEvent });
  }
});

更新

提供的代码在浏览器上完美运行,但从 Node 运行它时不起作用,因为在 Node.js 中运行 axios 时它使用 /lib/adapters/http.js 而不是 /lib/adapters/xhr.js

如果您阅读该代码,您将在 http 上看到它适配器没有任何 onUploadProgress选项。

我从浏览器测试了您的代码,访问了您的端点并且运行良好。 问题是当您运行从 Node.js 运行的集成测试时。

这里你有一个问题:https://github.com/axios/axios/issues/1966他们推荐使用的地方:progress-stream如果你想取得进展,但不会在 onUploadProress 上触发功能。

更新 2

我可以确认,在 puppeteer 上运行测试时,它使用的是 XMLHttpRequest 处理程序,而不是 Node.js。问题是您在异步回调中抛出错误,该错误未被 try/catch 捕获。 .

try {
  await request({
    method: 'post',
    url: `${protocol}://${host}:${port}/api/v1/add-file`,
    data: content,
    headers,
    onUploadProgress (progress) {
      // This error occurs in a different tick of the event loop
      // It's not catched by any of the try/catchs that you have in here
      // Pass a callback function, or emit an event. And check that on your test
      throw new Error(JSON.stringify(progress));
    }
  });

  return id;
} catch (ex) {
  if (!ex.response) {
    throw ex;
  }

  switch (ex.response.status) {
    case 401:
      throw new Error('Authentication required.');
    default:
      throw ex;
  }
}

如果你想捕捉到它,你必须将 axios 调用包装在 Promise 中。并在那里拒绝(这没有意义,因为那仅用于测试)。

所以我的建议是传递 onUploadProgress addFile 的参数, 这样你就可以检查它是否到达了 onUploadProgress来自你的测试。

为了看到:throw new Error(JSON.stringify(progress));被调用,使用 { headless: false } 启动 puppeteer .

这样你会看到错误被正确触发。

enter image description here

XMLHttpRequestUpload.onUploadProgress

关于javascript - axios onUploadProgress 和 onDownloadProgress 不适用于 CORS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55295036/

相关文章:

javascript - 从 getElementsByTagName() 获取属性的最佳方法?

node.js - 使用 ffmpeg 创建实时更新的视频流

python - Angular : PUT, OPTIONS 方法的 Flask RESTful 跨域问题

cors - 上传 Nifi 模板响应 403 错误 + 浏览器记录无效的 CORS 请求

angular - 如何在不更改后端的情况下解决 Angular 8 中的 CORS 问题?

javascript - jquery jquery-1.9.0 设置div显示:none when I trying to use toggle

javascript - 如何更改 jsTree 节点的文本?

javascript - jQuery 和 Node.js 有什么区别?

node.js - NextJS - 意外的 token 导入

javascript - 如何将单独的样式传递给 React 中的组件?