我编写了一个函数来使用 fetch 捕获非 200 个结果:
1 function $get(url, callback) {
2 fetch(url, {credentials: "same-origin"})
3 .then(resp => {
4 if (!resp.ok) {
5 resp.text().then((mesg) => {
6 throw {"stat": resp.status, "mesg": mesg.trim()}
7 })
8 return resp.text()
9 }
10 return resp.json()
11 })
12 .then(data => callback({"stat": 200, "data": data}))
13 .catch(error => callback(error))
14}
我在第 9 行出错:
ERROR: TypeError: Failed to execute 'text' on 'Response': body stream already read
我必须写第5~7行代码的原因是如果我写:
if (!resp.ok) {
throw {"stat": resp.status, "mesg": resp.statusText}
return resp.json()
我会收到类似 {"stat": 403, "mesg": "Forbidden"}
的错误消息,而我想要的是:
{"stat": 403, "mesg": "invalid user name or password"}
。
在服务器端,我的 go 程序会像这样生成非 200 回复:
> GET /api/login?u=asdf&p=asdf HTTP/1.1
> Host: localhost:7887
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 403 Forbidden
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Sat, 17 Jul 2021 11:53:16 GMT
< Content-Length: 25
<
invalid username or password
即go 库不会修改 http 状态文本,而是将错误消息放在 body 中,这可能是 http 标准规定的(例如,状态文本不能更改)。
所以,我的问题是:
=== 编辑 ===
以下代码可以正常工作,但是,正如注释所指出的那样,它似乎使用了“反”模式:
function $get(url, callback) {
fetch(url, {credentials: "same-origin"})
.then(resp => {
if (!resp.ok) {
resp.text().then((mesg) => {
callback({"stat": resp.status, "mesg": mesg.trim()})
})
return new Promise(function(_, _) {})
}
return resp.json()
})
.then(data => callback({"stat": 200, "data": data}))
.catch(error => { console.log(`GET ${url}\nERROR: ${error}`) })
}
但是,这行不通:
function $get(url, callback) {
fetch(url, {credentials: "same-origin"})
.then(resp => {
if (!resp.ok) {
resp.text().then((mesg) => {
throw `{"stat": resp.status, "mesg": mesg.trim()}`
})
}
return resp.json()
})
.then(data => callback({"stat": 200, "data": data}))
.catch(error => { console.log(`GET ${url}\nERROR: ${error}`) })
}
throw
将生成此错误,而不是将控制权传递给下面的 catch
:
127.0.0.1/:1 Uncaught (in promise) {"stat": resp.status, "mesg": mesg.trim()}
答案 0 :(得分:3)
考虑到您使用的是 fetch
,您还可以使用 async/await
并执行以下操作。 :
async function $get(url, callback) {
try {
const resp = await fetch(url, {credentials: "same-origin"});
if (!resp.ok) {
// this will end up in the catch statement below
throw({ stat: resp.status, mesg: (await resp.text()).trim());
}
callback({ stat: 200, data: await resp.json() });
} catch(error) {
callback(error);
}
}
我不明白你为什么要使用 callback
函数 :) 那些都是 1999 年
为了解释您的错误,您在出现错误时调用了 resp.text()
两次。为了防止这种情况,您应该立即返回从第一个 resp.text()
调用链接的承诺。这也会抛出错误并在没有到达连续的 then()
语句的情况下在 catch 块中结束:
function $get(url, callback) {
fetch(url, {credentials: "same-origin"})
.then(resp => {
if (!resp.ok) {
return resp.text().then((mesg) => {
// ^^^^^^
throw {stat: resp.status, error: mesg.trim()}
});
}
return resp.json() ;
})
.then(data => callback({stat: 200, data }))
.catch(error => callback(error))
}
不使用回调的“正确”$get
函数:
function $get(url) {
return fetch(url, {credentials: "same-origin"}).then(async (resp) => {
const stat = resp.status;
if (!resp.ok) {
throw({ stat, error: (await resp.text()).trim() });
}
return { stat, data: await resp.json() };
});
}
你可以像这样消费:
$get('https://example.com')
.then(({ stat, data }) => {
})
.catch({ stat, error}) => {
})
答案 1 :(得分:1)
只需返回被拒绝的承诺即可
1 function $get(url, callback) {
2 fetch(url, {credentials: "same-origin"})
3 .then(resp => {
4 if (!resp.ok) {
5 return resp.text().then((mesg) => {
6 throw {"stat": resp.status, "mesg": mesg.trim()}
7 })
9 }
10 return resp.json()
11 })
12 .then(data => callback({"stat": 200, "data": data}))
13 .catch(error => callback(error))
14}