I wrote a function to catch non-200 results with fetch:
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}
I got error on line 9:
ERROR: TypeError: Failed to execute 'text' on 'Response': body stream already read
The reason that I have to write code shown in line 5~7 is that if I wrote:
if (!resp.ok) {
throw {"stat": resp.status, "mesg": resp.statusText}
return resp.json()
I will get error message like {"stat": 403, "mesg": "Forbidden"}
, while what I want is:
{"stat": 403, "mesg": "invalid user name or password"}
.
On the server side my go program will generate non-200 reply like this:
> 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
I.e. the go library do not modify http status text, instead, put error message in body, which maybe mandated by the http standard (e.g. status text cannot be changed).
So, my question is, either:
- How to read the body of non-200 reply without using promise?
- or, how to reply an "empty" promise in case of error, to prevent the stream being read again?
=== EDIT ===
The following code works OK, however, it seems to use "anti" pattern as pointed out by comments:
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}`) })
}
However, this doe not work:
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}`) })
}
The throw
will generate this error, instead of passing control to the catch
below:
127.0.0.1/:1 Uncaught (in promise) {"stat": resp.status, "mesg": mesg.trim()}