Gol*_*den 8 javascript xmlhttprequest node.js cors axios
我有一个用Node.js编写的服务器,以及一个在浏览器中运行的Web客户端。客户端应从服务器上载一些文件并将其下载到服务器。服务器不是最初交付客户端的服务器,因此这里存在跨域情况。
服务器使用cors中间件来启用跨域请求。由于我还使用了许多自定义标头,因此将其与如下配置一起使用:
api.use(cors({
origin: '*',
allowedHeaders: [ 'content-type', 'authorization', 'x-metadata', 'x-to' ],
exposedHeaders: [ 'content-type', 'content-disposition', 'x-metadata' ]
}));
Run Code Online (Sandbox Code Playgroud)
相反,客户端使用axios,并且上传文件的代码如下所示:
await axios({
method: 'post',
url: 'https://localhost:3000/api/v1/add-file',
data: content,
headers: {
// ...
}
});
Run Code Online (Sandbox Code Playgroud)
到目前为止,一切都按预期工作,并且特别适用于CORS。
现在,我想跟踪上传的进度,因此,如其文档onUploadProgress所述axios,我将回调添加到了调用中:
await axios({
method: 'post',
url: 'https://localhost:3000/api/v1/add-file',
data: content,
headers: {
// ...
},
onUploadProgress (progressEvent) {
console.log({ progressEvent });
}
});
Run Code Online (Sandbox Code Playgroud)
如果我现在运行客户端,则上载仍然有效–但onUploadProgress永远不会调用回调。我已经了解到,并不是所有的浏览器都支持此功能(内部仅绑定到onprogress基础XmlHttpRequest对象的事件),但是最新的Chrome应该可以做到这一点(如果我尝试使用不涉及CORS的其他设置,则一切似乎都可以做到)工作)。因此,我认为这是一个CORS问题。
我读到,一旦您向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
}));
Run Code Online (Sandbox Code Playgroud)
结果:上载仍然有效,但仍未onUploadProgress引发任何事件。因此,我认为我缺少与CORS相关的内容。问题是:我想念什么?
我也尝试过使用更大的文件(几乎2 GB,而不是几个KB)重新运行相同的设置,但是结果是相同的。有谁知道我在这里做错了什么?
回答一些评论:首先,我的axios版本是0.18.0,因此我使用的是最新的稳定版本。
服务器的源代码可以在存储库thenativeweb / wolkenkit-depot中找到。在此配置了 CORS部分。请注意,这与我上面发布的版本略有不同,因为我做了一些本地更改。但是,这是您需要处理的地方。
要构建服务器,您需要安装Node.js和Docker。然后,运行:
$ npm install
$ npx roboter build
Run Code Online (Sandbox Code Playgroud)
这将产生一个新的Docker映像,其中包含服务器的代码(您将需要此Docker映像才能运行下面的客户端测试)。请注意,如果您更改服务器上的任何内容,则必须再次重建该Docker映像。
客户端可以在thenativeweb / wolkenkit-depot-client-js存储库中的分支“ 发行-145-通知-关于-进展-何时上传或下载文件”中找到。
在文件DepotClient.js中,我向添加了一个事件侦听器onUploadProgress,该事件侦听器应该简单地引发一个异常(这反过来应该使事件明显可见)。
然后,通过此测试运行该代码(对此有更多测试,但这只是其中之一),这应导致异常,但不是。
要再次运行测试,您必须安装Node.js和Docker,并且必须按照前面所述构建服务器。然后使用以下命令代表您运行测试:
$ npm install
$ npx roboter test --type integration
Run Code Online (Sandbox Code Playgroud)
我的期望是至少要进行一项addFile测试。事实是,它们全部变为绿色,这意味着上传本身可以完美运行,但onUploadProgress从未引发该事件。以防万一您对测试的执行环境有所怀疑:我在这里使用puppeteer,这是无头的Chrome。
Since that code works perfectly fine (both with or without cors), I'm going to assume that you're using an old axios version. onUploadProgress was added on version 0.14.0.
If you're using an older version, you can use: progress instead of onUploadProgress, or just update to the latest version.
0.14.0 (Aug 27, 2016)
BREAKING Splitting
progressevent handlers intoonUploadProgressandonDownloadProgress(#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 });
}
});
Run Code Online (Sandbox Code Playgroud)
UPDATE.
The code provided works perfectly on the browser, but it does not work when running it from node, because when running axios in Node.js it uses /lib/adapters/http.js instead of /lib/adapters/xhr.js
If you read that code, you will see that on the http adapter there isn't any onUploadProgress option.
I tested your code from a browser, hitting your endpoint and it works fine. The issue is when you're running the integration tests that is running from Node.js.
Here you have an issue on that: https://github.com/axios/axios/issues/1966 where they recommend using: progress-stream if you want to have a progress, but it won't be triggered on onUploadProress function.
UPDATE 2
I can confirm that when running the test on puppeteer, it's using XMLHttpRequest handler, not Node.js. The issue is that you're throwing the error inside an async callback, that is not catched, by the 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;
}
}
Run Code Online (Sandbox Code Playgroud)
If you want to catch that, you will have to wrap the axios call in a Promise and reject in there (Which doesn't make sense, since that's for testing only).
So my recommendation is to pass an onUploadProgress argument to addFile, that way you can check if it's reaching the onUploadProgress from your tests.
In order to see that: throw new Error(JSON.stringify(progress)); was being called, launch puppeteer with { headless: false }.
That way you will see that the error is being triggered correctly.
XMLHttpRequestUpload.onUploadProgress
| 归档时间: |
|
| 查看次数: |
1603 次 |
| 最近记录: |