我正在尝试调用 Google Drive REST API 以使用
fetch
API 从浏览器上下文上传文件,但无法克服一个奇怪的错误。
TypeError: Failed to fetch
查看有关该主题的其他帖子,有些指出 CORS 失败,但很难从该消息中准确确定错误是什么。奇怪的是,查看 Chrome 开发工具的“网络”选项卡会讲述一个不同的故事:
现在我将第一个承认我发送格式错误的请求的可能性(很高?),但是在没有任何错误消息的情况下确定请求格式错误也非常困难。
令我更加困惑的是,为什么
fetch
调用被拒绝(用 await
抛出错误),而不是通过 400 响应来解决。
拨打电话的代码非常基本:
await fetch(`https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart`, options);
options
对象是非常样板的,只设置诸如Authorization
标题、Content-Type
、Content-Length
之类的东西,以及主体(在本例中)是multipart/related
所以我既没有得到优雅的
resolve
消息回复,也没有得到有用的reject
结果,而只是TypeError: Failed to fetch
如果我仅依靠拒绝,我不会知道这实际上是 400。只有网络工具揭示了这一点。 有人知道为什么这个 400 错误会被“吞没”吗?我需要做什么才能捕获它?
其他编辑
看起来像这样:
async send() {
this._build();
return await fetch(this._parameterizedUrl, this._options);
}
它包含在一个对象中,该对象仅封装了创建请求的过程(称为
Request
)。所以
this
指针指的是封装对象。 this._parameterizedUrl
只是已经发布的 URL 字符串的值,this._options
是已经提到的选项对象。对 build()
的调用只是将 Request
对象的值组装到 options
参数中(但也支持 URL 参数,在本例中未使用)调用此函数的代码如下所示:
const request = Request.post(uploadUrl);
request.setContentType(contentType);
request.setBody(body);
request.setAccessToken(accessToken);
request.addHeader('Content-Length', contentLength);
const response = await request.send();
Request.post(uploadUrl)
调用是创建我的
Request
对象的简写(这是我自己的封装,如上所述)此调用将抛出一个错误,该错误在堆栈中较早的位置被捕获。 Failed to fetch
错误的跟踪的下一行如下所示:
TypeError: Failed to fetch
at Request.send (api.js:240:1)
完整再现
document.addEventListener('DOMContentLoaded', async () => {
const url = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart';
const options = {};
const authToken = '<Add Auth Token>';
const boundary = 'boundary-' + Math.random().toString(36).substring(2);
const contentType = `multipart/related; boundary=${boundary}`;
// Body part meta data for the filename
const meta = {
name: 'test.json'
};
// Body part for media (the file)
const data = {
foo: "bar",
bar: "baz"
}
const metaBlob = new Blob([JSON.stringify(meta)], {
type: 'application/json',
});
const mediaBlob = new Blob([JSON.stringify(data)], {
type: 'application/json',
});
const body = new FormData();
body.append('metadata', metaBlob, "metadata");
body.append('media', mediaBlob, name);
let contentLength = metaBlob.size + mediaBlob.size;
const headers = new Headers();
headers.append('Authorization', `Bearer ${authToken}`);
headers.append('Content-Type', contentType);
headers.append('Content-Length', contentLength);
options['method'] = 'POST';
options['headers'] = headers;
options['body'] = body;
try {
const response = await fetch(url, options);
console.log(response);
} catch (err) {
console.error(err);
}
});
修改后的脚本:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Google Drive Test</title>
<script>
document.addEventListener('DOMContentLoaded', async () => {
const url = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart';
const options = {};
const authToken = '<Add Auth Token>';
// Body part meta data for the filename
const meta = {
name: 'test.json'
};
// Body part for media (the file)
const data = {
foo: "bar",
bar: "baz"
}
const metaBlob = new Blob([JSON.stringify(meta)], {
type: 'application/json',
});
const mediaBlob = new Blob([JSON.stringify(data)], {
type: 'application/json',
});
const body = new FormData();
body.append('metadata', metaBlob, "metadata");
body.append('media', mediaBlob, name);
const headers = new Headers();
headers.append('Authorization', `Bearer ${authToken}`);
options['method'] = 'POST';
options['headers'] = headers;
options['body'] = body;
try {
const response = await fetch(url, options);
const res = await response.json();
console.log(res);
} catch (err) {
console.error(err);
}
});
</script>
</head>
<body></body>
</html>
运行此脚本,得到以下结果。
{
"kind":"drive#file",
"id":"###",
"name":"test.json",
"mimeType":"application/json"
}