【问题标题】:JavaScript: Is there a way to detect if a response contains error messages in json format when it is not okJavaScript:有没有办法在响应不正确时检测响应是否包含 json 格式的错误消息
【发布时间】:2021-06-25 00:49:15
【问题描述】:

我正在为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'))
    }
  })
}

它工作正常。问题在于这个 sn-p

 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'))
    }
  })

如果响应不正确,它会使用通用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()
      }

有人将我的问题标记为与this question 重复。事实上,它们并不相同。我没有做出与该问题相同的假设,即 API 调用在成功和失败时都返回 JSON 数据。我的问题是关于在我们不能做出这样的假设的情况下我们应该怎么做。

【问题讨论】:

  • 这能回答你的问题吗? fetch: Reject promise with JSON error object
  • @FredStark 不,它没有。链接指向的问题假设我请求总是在成功和失败时返回 JSON 数据。我的问题没有那个假设,实际上它是在问我们在无法做出假设的情况下应该如何做。请恢复您的投票以结束我的问题。
  • 哦,我可能错过了这个案例,但您根本无法控制后端? JSON 不是发送错误消息的唯一方式,您是否愿意同时处理原始文本消息或 HTML 错误页面?
  • @Joji 啊,好吧,我知道它有什么不同。投票被撤回。看起来你现在也得到了一半体面的答案:)

标签: javascript json http


【解决方案1】:

响应不是 ok 并不能阻止您将其主体作为 JSON 使用,因此您的最后一个 sn-p 确实应该如何处理。

现在你要求一些“更优雅”的东西,它可能不会更优雅,但写同样的东西的一种更少冗余的方式是:

fetch(endpoint, config as any).then(async (response) => {
  if (response.ok) {
    return response.json(); // there is no need to await the return value
  }
  else {
    const err_message = await response.json() // either we have a message
      .catch( () => new Error( "Unknown Error" ) ); // or we make one
    return Promise.reject( err_message );
  }
})

关于最后一个问题,是的,两者都是等价的,await 版本在微任务队列中再进行一次往返,并使您的代码更长一些,但对于所有这些问题,我们可以说它们是相同的.

【讨论】:

  • 您好,感谢您的回答。我可以问为什么即使响应的正文没有 JSON,调用 response.json() 也不会抛出?或者只要请求 'content-type' 是 'application/json',响应体总是会是一个有效的 json,即使它不正常??
  • 它会抛出,嗯,返回的 Promise 会拒绝,这就是为什么我链接了 .catch() 来设置你自己的错误值。另一个答案是错误的。
  • 或者,您也可以在 else 语句中使用 return response.json().catch(() => new Error("Unknown Error")).then(error => { throw error }),然后删除 async/await
【解决方案2】:

我会进一步简化 Kaiido,修复一些错误,并添加自定义错误处理程序。

class ApiCallError extends Error {

  response: Response;
  body: any;
  httpStatus: number;

  constructor(response, body) {

    super('HTTP error: ' + response.status);
    this.httpStatus = response.status;
    this.response = response;
    this.body = body;

  }  

}
async function apiCall(endpoint: string, options?: any) {

  const config = {
    // do your magic here
  }

  const response = await fetch(endpoint, config);

  if (!response.ok) {
    throw new ApiCallError(response, await response.json());
  }
  return response.json();
}

变化:

  1. 如果您只是要再次抛出错误,则无需捕获错误。
  2. 如果您支持 await,则不需要 .then(),这将使您的代码更简单。
  3. Promise.resolvePromise.reject 真的没有意义,你可以只使用returnthrow
  4. 你不应该返回简单的错误,你应该抛出它们(或确保它们被包裹在一个拒绝承诺中)
  5. 尽管在 javascript 中您可以“抛出任何东西”,包括字符串和任意对象,但最好抛出 Error 或扩展的东西,因为这会给您一个堆栈跟踪。
  6. 使自定义错误类中的所有错误信息都可用,从而为apiCall 的用户提供了可能需要的一切。

【讨论】:

  • 如果 response.json() 不是一个有效的 JSON,那么这将拒绝一个 SyntaxError JSON.parse 错误而不是预期的 ApiCallError 错误。
  • @Kaiido 是的,你是对的。所以这真的取决于你的期望。也许 OP 总是打算发出 JSON 错误,并且如果服务器没有发出 JSON,想要那个错误。
  • @Kaiido 在你的例子中,这种情况变成了Unknown Error,我认为我宁愿得到一个与 JSON 解析相关的错误,而不是一个未知的错误。
  • 我只是重写了OP的逻辑,我没有做到。
  • @Kaiido 抱歉,忘记了最初的问题。无论如何,我支持它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-12
  • 1970-01-01
  • 2021-04-08
  • 2019-05-25
  • 2023-03-22
  • 1970-01-01
  • 2011-08-22
相关资源
最近更新 更多