Home > Blockchain >  In JS fetch API promise style, how to get the raw body when the json() function failed
In JS fetch API promise style, how to get the raw body when the json() function failed

Time:07-21

I know this can be solved by writing all codes to async-await style, then can simply write let text = await res.text(); then try catch the JSON.parse(text) and then do decision.
But here I just want to know if there is any way we can achieve that in .then/.catch style.

Consider the below code:

async function test() {
  try {
    let n = await fetch("https://stackoverflow.com")
    .then(res => {
      return res.json()
    })
    .then(data => data.results.length)
    .catch(e => {
      console.error("Catch 2", e)
    })
  }
  catch (e) {
    console.error("Catch 3", e)
  }
}

if we execute this function in the browser devtools(F12) with await test(), then there will be an error catch by the "Catch 2" clause. But in the error detail we can only see some logs like JSON parse error. We cannot see the full text of the response body. Is there any way that can get the text when the JSON parsing failed?

CodePudding user response:

Your best bet is to look at the response in your devtools' network tab. That will show you the full response.

But if you want to do it in code, you can separate reading the response from parsing it by using the text method instead of the json method, then parsing the text yourself.

The parsing error may be down to the fact you aren't checking for HTTP success. As I noted on my old anemic blog here, fetch only rejects its promise on network errors, not HTTP errors (like 404, 500, etc.). To check for HTTP success, look at the ok or status properties.

Here's the minimal-changes version separating reading the response from parsing it, and checking for HTTP success before reading it at all:

async function test() {
    try {
        let n = await fetch("https://stackoverflow.com")
            .then((res) => {
                if (!res.ok) {                                      // ***
                    throw new Error(`HTTP error ${res.status}`);    // ***
                }                                                   // ***
                return res.text(); // ***
            })
            .then((text) => {
                // *** you can look at `text` here in a debugger, or
                // *** log it, save it, etc., before parsing below
                // *** (which might throw an error)
                try {
                    const data = JSON.parse(text); // ***
                    return data.results.length;
                } catch (error) {
                    console.error("Parsing error", e);
                    console.error("Text we were parsing:", text);
                }
            })
            .catch((e) => {
                console.error("Catch 2", e);
            });
        // ...do something with `n`...
    } catch (e) {
        console.error("Catch 3", e);
    }
}

But a couple of things there:

  1. I wouldn't mix async/await with explicit promise callbacks like that.

  2. With that and with your original code, errors will result in n receive the value undefined, because the catch handlers (and my new try/catch block in the then handler) don't return anything.

Instead:

async function test() {
    try {
        const res = await fetch("https://stackoverflow.com");
        if (!res.ok) {
            throw new Error(`HTTP error ${res.status}`);
        }
        const text = await res.text();
        // *** you can look at `text` here in a debugger, or
        // *** log it, save it, etc., before parsing below
        // *** (which might throw an error)
        try {
            const data = JSON.parse(text);
            const n = data.results.length;
            // ...do something with `n`...
        } catch (error) {
            console.error("Parsing error", e);
            console.error("Text we were parsing:", text);
        }
    } catch (e) {
        console.error("Catch 3", e);
    }
}

Or if you want to respond differently to the parsing error, wrap that bit in a try/catch, etc.

CodePudding user response:

You shouldn't confuse the catch which catching errors in the fetch function itself - with the response errors

fetch("/developer.mozilla.org")
    .then(res => {
        if (!res.ok) {
         console.log("there was an error also here")     <= this code also runs
         console.log("response is", res);
      }
      
      return res.json()
    })
    .then(data => data.results.length)
    .catch(e => {     
      console.error("Catch 2", e);
    })

In your case, you tried converting data -> JSON w/o success, it failed and dropped to the "catch" section. but to inspect the response - you can dump it in the first section above where I added res.ok

CodePudding user response:

I believe you could do something like this when using promise style Javascript:

const fetchDataPromise = () => {
    fetch('https://stackoverflow.com').then((res) => {
        res.json().then((jsonData) => {
            console.log(jsonData)
        }).catch((err) => {
            console.error(err)
            res.text().then((rawData) => {
                console.log(rawData)
            }).catch((err) => console.error(err))
        })
    })
 }

Also more intuitive approach would be to use async/await (the trade-off is that you will have to do the API call again):

const fetchData = async () => {
    try {
        const res = await fetch('https://stackoverflow.com')
        const jsonData = await res.json()
        console.log(jsonData)
    } catch (err) {
        try {
            console.error(err)
            const res = await fetch('https://stackoverflow.com')
            const rawData = await res.text()

            console.log(rawData)
        } catch (rawError) {
            console.error(rawError)
        }

    }
 }
  • Related