V S*_*S X 15 javascript error-handling http fetch reactjs
我在后端有一个 API,它201在成功调用的情况下发送状态,如果提交的数据有任何错误,它会发送状态422(不可处理的实体),并带有如下 json 响应:
{
"error": "Some error text here explaining the error"
}
Run Code Online (Sandbox Code Playgroud)
此外,它会404在 API 由于某种原因无法在后端工作时发送。
在前端,我使用以下代码获取响应并根据响应状态代码执行成功或错误回调:
{
"error": "Some error text here explaining the error"
}
Run Code Online (Sandbox Code Playgroud)
该successCallback函数能够在状态代码的情况下以正确的 JSON 格式接收响应,201但是当我尝试在errorCallback( status: 422) 中获取错误响应时,它显示如下内容(在控制台中记录响应):
Error: [object Response]
at status (grocery-market.js:447)
Run Code Online (Sandbox Code Playgroud)
当我console.log在new Error()像这样包装之前尝试错误响应时,
checkStatus = (response) => {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
console.log(response.json()) //logging the response beforehand
return Promise.reject(new Error(response.statusText))
}
}
Run Code Online (Sandbox Code Playgroud)
我在控制台中得到以下承诺(该[[PromiseValue]]属性实际上包含我需要的错误文本)
有人可以解释为什么这只会在错误情况下发生,即使我response.json()在错误和成功情况下都打电话?
我怎样才能以干净的方式解决这个问题?
编辑:
我发现如果我json()在错误响应上创建另一个承诺,我能够得到正确的错误:
fetch("api_url_here", {
method: 'some_method_here',
credentials: "same-origin",
headers: {
"Content-type": "application/json; charset=UTF-8",
'X-CSRF-Token': "some_token_here"
}
})
.then(checkStatus)
.then(function json(response) {
return response.json()
})
.then(function(resp){
successCallback(resp)
})
.catch(function(error){
errorCallback(error);
});
//status function used above
checkStatus = (response) => {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response))
}
}Run Code Online (Sandbox Code Playgroud)
但是为什么我必须.json()在错误情况下调用另一个响应?
简答
因为您对响应内容解析两次的假设是不正确的。在原始代码段中,当满足错误条件时跳过thenafter then(checkStatus)。
长答案
一般来说,结构良好的承诺链包括:
then仅在完成Promise时运行的处理程序catch仅在拒绝Promise时运行的处理程序finally(可选)在 Promise结算时运行(在 2 或 3 中)2 和 3 中的每个处理程序都返回一个 Promise以启用链接。
接下来,fetchFetch API 的方法仅在网络故障时拒绝,因此then无论status响应代码如何,都会调用第一个方法。您的第一个处理程序,onFulfilled回调返回已完成或拒绝的 Promise。
如果满足,它将控制权传递给then链中的下一个方法调用,在那里您通过调用json响应上的方法提取 JSON,然后将其作为 Promise 值传递then给要在successCallback.
如果被拒绝,则控制将传递给catch方法调用,该调用接收具有设置为该值的 Promise,new Error(response)然后您立即将其传递给errorCallback。因此,后者接收 的实例Error,其值是Response来自 Fetch API的实例。
这正是您所看到的记录:Error: [object Response],toString在 的实例上调用方法的结果Error。第一部分是构造函数名称,第二部分是内容的字符串标记(形式为 [type Constructor])。
该怎么办?
由于您的 API 为每个可能的用例 ( 201, 404, 422)返回 JSON 响应,因此将解析的响应传递给已完成和已拒绝的承诺。另外请注意,你不小心宣布checkStatus通过省略在全球范围内var,const或let关键字:
//mock Response object
const res = {
status: 200,
body: "mock",
async json() {
const {
body
} = this;
return body;
}
};
const checkStatus = async (response) => {
const parsed = await response.json();
const {
status
} = response;
if (status >= 200 && status < 300) {
return parsed;
}
return Promise.reject(new Error(parsed));
};
const test = () => {
return checkStatus(res)
.then(console.log)
.catch((err) => console.warn(err.message))
.finally(() => {
if (res.status === 200) {
res.status = 422;
return test();
}
});
};
test();Run Code Online (Sandbox Code Playgroud)
此外,由于您已经使用了 ES6 特性(根据箭头函数的存在来判断),为什么不一路使用语法糖async/await提供:
(() => {
try {
const response = await fetch("api_url_here", {
method: 'some_method_here',
credentials: "same-origin",
headers: {
"Content-type": "application/json; charset=UTF-8",
'X-CSRF-Token': "some_token_here"
}
});
const parsed = await response.json(); //json method returns a Promise!
const {
status
} = response;
if (status === 201) {
return successCallback(parsed);
}
throw new Error(parsed);
} catch (error) {
return errorCallback(error);
}
})();
Run Code Online (Sandbox Code Playgroud)
请注意,当您将parsedJSON 内容传递给Error()构造函数时,ToString会调用一个抽象操作(请参阅ECMA 规范的步骤 3a)。
如果消息是一个对象,除非原来的对象有toString方法,否则会得到一个字符串标记,即[object Object],导致错误处理程序无法访问该对象的内容:
(() => {
try {
const response = await fetch("api_url_here", {
method: 'some_method_here',
credentials: "same-origin",
headers: {
"Content-type": "application/json; charset=UTF-8",
'X-CSRF-Token': "some_token_here"
}
});
const parsed = await response.json(); //json method returns a Promise!
const {
status
} = response;
if (status === 201) {
return successCallback(parsed);
}
throw new Error(parsed);
} catch (error) {
return errorCallback(error);
}
})();
Run Code Online (Sandbox Code Playgroud)
相反,如果您reject将 Promise 对象作为参数传递,则被拒绝的 Promise[[PromiseValue]]将是对象本身。
| 归档时间: |
|
| 查看次数: |
2875 次 |
| 最近记录: |