Fetch api - получение тела json для обоих блоков и блоков catch для отдельных кодов состояния

Я использую fetch api для получения URL, который может вернуть:

Ответ: status = 200, json body = {'user': 'abc', 'id': 1}

или же

Ответ: status = 400, json body = {'причина': 'некоторая причина'}

или же

Ответ: status = 400, json body = {'причина': 'какая-то другая причина'}

Я хочу сделать отдельную функцию request() что я использую из различных частей моего кода следующим образом:

    request('http://api.example.com/').then(
        // status 200 comes here
        data => // do something with data.id, data.user
    ).catch(
        // status 400, 500 comes here
        error =>  // here error.reason will give me further info, i also want to know whether status was 400 or 500 etc
    )

Я не могу сделать разделение между 200 и 400 500 (я попытался, выдав ошибку). Когда я выбрасываю ошибку, мне все еще трудно извлечь тело JSON (использовать для error.reason).

Мой текущий код выглядит следующим образом:

import 'whatwg-fetch';

/**
 * Requests a URL, returning a promise
 */
export default function request(url, options={}) {

    console.log('sending api request, url = ' + url)

    return fetch(url, options)
        .then(checkStatus)
        .then(parseJSON)
        .then((data) => ({data}))
        .catch((err) => ({err}));
}


function checkStatus(response) {
    if (response.status >= 200 && response.status < 300) {
        return response;
    }

    const error = new Error(response.statusText);
    error.response = response;
    throw error;
}



function parseJSON(response) {
    return response.json();   // json() is a promise itself
}

Я попытался решить эту проблему следующим образом, инвертировав порядок .then() звонит, но не работает

export default function request(url, options) {
    return fetch(url, options)
        .then(parseJSON)        // note that now first calling parseJSON to get not just JSON but also status. 
        .then(checkStatus)      // i.e. Inverted order of the two functions from before

        .then((data) => ({data}))
        .catch((err) => ({err}));
}

function checkStatus({data, status}) {

    if (status >= 200 && status < 300) {
        return data;
    }
    else {
        // const error = new Error(response.statusText);
        const error = new Error("Something went wrong");
        // error.response = response;
        error.data = data;

        throw error;
    }

}

function parseJSON(response) {
    let jsonBody

    response.json().then(json => {
        jsonBody = json                 // this does not help, i thought it will make jsonBody fill up, but seems its in a diff thread
    })              

    return {
        data: jsonBody,
        status: response.status     // my aim is to send a whole dict with status and data to send it to checkStatus, but this does not work
    }
}

2 ответа

Решение

response.json() возвращает асинхронный результат. Вы не возвращаете объект в parseJSON изнутри .then() прикован к response.json(), Чтобы исправить эту проблему, вы можете вернуться response.json() обещать в parseJSON вызов и возврат объекта, содержащего data а также status изнутри .then() прикован к response.json()

function parseJSON(response) {
    return response.json().then(json => {
          return {
                   data: json,
                   status: response.status  
                 }
    })         
}  

Вот немного другой подход: с помощью одной строки я создаю обещание, похожее на ответ, с помощью ok, status и json-as-object (не обещание), а затем я решаю, что делать с этим объектом. Обычно я отклоняю с ответом, если response.ok имеет значение false, в противном случае я разрешаю только с помощью данных json. Сетевые ошибки /json-parse-errors отклоняются как обычно.

fetch(url, options)
    .then(r => r.json().then(json => ({ok: r.ok, status: r.status, json})))
    .then( r => r.ok ? r.json: Promise.reject(r))
Другие вопросы по тегам