Простая оболочка вокруг выборки

Я пишу простую обертку вокруг fetch.

async function apiCall(
  endpoint: string,
  {
    data,
    headers: customHeaders,
    ...customConfig
  }: { data?: Object; headers?: Object } = {}
) {
  const config = {
    method: data ? 'POST' : 'GET',
    body: data ? JSON.stringify(data) : undefined,
    headers: {
      'content-type': data ? 'application/json' : undefined,
      ...customHeaders,
    },
    ...customConfig,
  }

  return fetch(endpoint, config as any).then(async (response) => {
    if (response.ok) {
      const json = await response.json() // 🤔
      return json
    } else {
    // 👇 🚨 what if `response` contains error messages in json format?
      return Promise.reject(new Error('Unknown Error'))
    }
  })
}

Работает нормально. Проблема в этом фрагменте:

 return fetch(endpoint, config as any).then(async (response) => {
    if (response.ok) {
      const json = await response.json()
      return json
    } else {
    // 👇 🚨 what if `response` contains error messages in json format?
      return Promise.reject(new Error('Unknown Error'))
    }
  })

Если response не в порядке, он отклоняет с общим Error. Это потому, что по умолчанию window.fetch отклонит обещание только в том случае, если фактический сетевой запрос не удался. Но проблема в том, что даже если response это не нормально, сообщения об ошибках могут появляться в json формат. Это зависит от деталей реализации серверной части, но иногда вы можете получить сообщения об ошибках в теле ответа, response.json(). Теперь этот вариант использования не описан в созданной мной оболочке.

Так что мне интересно, как я смогу это объяснить? Я думаю, ты можешь сделать что-то вроде

fetch(endpoint, config as any).then(async (response) => {
      if (response.ok) {
        const json = await response.json()
        return json
      } else {
          try {
            const json = await response.json()
            return Promise.reject(json)
          } catch {
            return Promise.reject(new Error('Unknown Error'))
          }
      }
    })

Интересно, есть ли более элегантный способ сделать это?

Наконец, я очень хорошо знаком с такими библиотеками, как Axios. Я построил это частично, чтобы удовлетворить свое интеллектуальное любопытство.

Кстати, вопрос, не имеющий отношения к делу, но мне интересно, эквивалентны ли следующие фрагменты?

 if (response.ok) {
        const json = await response.json()
        return json
      }
 if (response.ok) {
        return response.json()
      }

1 ответ
1

Если вам нужно учитывать смешанные типы ответов (в вашем случае «неизвестные ошибки» или ошибки в формате JSON), вы можете рассмотреть возможность проверки Content-Type ответа. Видеть это Ответ StackOverflow для справки. Это, конечно, доверяет серверу, отображающему правильный заголовок типа содержимого для ответа.

Как вы упомянули, fetch не отклоняет статус ошибки HTTP, вместо этого response.ok будет установлен на false. Вы можете использовать это, чтобы решить, хотите ли вы отклонять или же разрешить ваше обещание.

/* ... */.then(async response => {
    const contentType = response.headers.get("content-type");
    if (contentType && contentType.indexOf("application/json") !== -1) {
        const json = await response.json();
        return response.ok
            ? Promise.resolve(json)
            : Promise.reject(json);
    } else {
        const text = await response.text();
        return response.ok
            ? Promise.resolve(text)
            : Promise.reject(new Error("Unknown Error!"));
    }
});

Вы также можете (о чем говорится в сообщении, указанном выше) использовать try-catch блокировать, если вы не доверяете заголовкам ответов.

/* ... */.then(async response => {
    const text = await response.text();
    try {
        const data = JSON.parse(text);

        // If the line above has not produced an error, the response contained valid JSON.
        // We can either choose to just return the resulting data, or, as in
        // example above, resolve/reject the Promise based on the status code.

        return response.ok
            ? Promise.resolve(data);
            : Promise.reject(data);
    }
    catch(err) {
        // This means that the response probably contained text. As above, you can
        // decide if this means an error has occurred, or if the result will just
        // be handled differently.

        return response.ok
            ? Promise.resolve(text);
            : Promise.reject(new Error("Unknown Error!"));
    }
});

Что касается вашего дополнительного вопроса о разнице между:

if (response.ok) {
    const json = await response.json();
    return json;
}
// ... and ...
if (response.ok) {
    return response.json();
}

По сути, это сводится к разнице между:

return await response.json();
// ... and ...
return response.json();

Разница заключается в том, как JavaScript обрабатывает async/await методы. Первая строка возвращает общий T, а второй возвращается Promise<T>. Это означает, что первый «задерживает» выполнение return заявление до тех пор, пока обещание, возвращенное response.json() разрешен, в то время как второй говорит: «Меня не волнует, что выполнение продолжается, тот, кто позвонит мне позже, должен будет ждать, пока мое значение будет разрешено.», что происходит, когда вы выполняете .then(...) по обещанию.

Обратите внимание, что первый метод по-прежнему в конечном итоге возвращает Promise<T> хотя, поскольку async По сути, методы — это просто причудливые оболочки для поколений обещаний.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *