为什么我的 Axios 实例没有 return 捕获错误的响应?

Why does my instance of Axios not return the response in a caught error?

我目前正在进行一个项目,为 Udemy 讲师创建客户端 API。

我用 Vue 编写了客户端,使用 Axios 作为我的 HTTP 客户端。

我已将不同的 API 请求抽象为 ES6 化的 API 包装器库 (Udemy.js) 中的函数,以便于重用它们。

Udemy.js 首先初始化一个 Axios 实例,然后导出 API 使用该实例作为基础的函数作为承诺。

下面是从文件中提取的,尽管为了便于阅读,我已经删除了模块导出的所有功能,除了一个功能(并且显然编辑了 API 标记)。端点 URI 包含 "message-threadssss" — 这是故意的,目的是让服务器 return 404:

import axios from 'axios';

const token = '***************************';
const axiosOptions = {
  baseURL: 'https://www.udemy.com/instructor-api/v1',
  timeout: 10000,
  headers: {
    Accept: '*/*',
    'Content-Type': 'application/json;charset=utf-8',
    Authorization: `Bearer ${token}`,
  },
};

const axiosInstance = axios.create(axiosOptions);

export default {
  postMessage(messageThreadId, messageBody) {
    return axiosInstance
            .post(`/message-threadssss/${messageThreadId}/messages/`, {
              content: messageBody,
            })
            .then(response => response.data)
            .catch(error => error);
  },
}

UdemyApi.postMessage(threadId, threadReply);
.then((response) => {
        this.isLoading = false;
        this.sentReply = response;
        this.replyBody = '';
        this.$root.$emit('reply-sent', {
          threadId: this.thread.id,
          sentReply: this.sentReply,
        });
      })
      .catch((error) => {
        if (error.response) {
          // Case 1 (Server returned error)
          console.log(error.response.data);
          console.log(error.response.status);
          console.log(error.response.headers);
        } else if (error.request) {
          // Case 2 (Pre-response error)
          console.log(error.request);
        } else {
          // Case 3 (Mysterious error)
          console.log('Error:', error.message);
        }
        this.$root.$emit('show-snackbar', {
          message: `Failed to send. ${error} `,
          actionText: 'Understood. :(',
        });
        this.isLoading = false;
      });

请求发送没有问题,如果请求成功(即 2xx),Vue 组件能够访问 then() 块中的响应数据。

当服务器 return 出现错误(在本例中为 404)时,我希望捕获的错误包含一个 response 对象(案例 1)。

相反,没有 response 对象被 return 编辑错误(案例 2),这使我无法正确处理它。当请求确实导致服务器响应 404 错误时,就会发生这种情况:

HTTP/2.0 404 Not Found
content-type: text/json

我读到如果 Axios 应用了拦截器,可能会导致这个问题,但在这种情况下,我没有应用任何拦截器。

总而言之,我有点不知所措。如何让服务器的响应进入我的 Vue 组件?

编辑(2 月 6 日)

我没有在我的初始 post 中包含所有有用的控制台输出,所以在这里。执行的 console.log() 行是案例 2 行(只添加了一个控制台日志条目,没有前缀 "Error: ",就像案例 3 中的情况一样):

12:22:28.748
XMLHttpRequest
    mozAnon: false
    mozSystem: false
    onabort: null
    onerror: function handleError()
    onload: null
    onloadend: null
    onloadstart: null
    onprogress: null
    onreadystatechange: function handleLoad()
    ontimeout: function handleTimeout()
    readyState: 4
    response: ""
    responseText: ""
    responseType: ""
    responseURL: ""
    responseXML: null
    status: 0
    statusText: ""
    timeout: 100000
    upload: XMLHttpRequestUpload { onloadstart: null, onprogress: null, onabort: null, … }
    withCredentials: false
    <prototype>: XMLHttpRequestPrototype { open: open(), setRequestHeader: setRequestHeader(), send: send(), … }
replybox.vue:72

编辑 2(2 月 6 日)

如果我从 postMessage() 定义中删除 then()catch() 如下所示:

 postMessage(messageThreadId, messageBody) {
    return axiosInstance
            .post(`/message-threadssss/${messageThreadId}/messages/`, {
              content: messageBody,
            });
  },

然后简化 postMessage() 调用的 catch() 块,只输出 error 对象,如下所示:

    .catch((error) => {
        console.log(error);
        this.$root.$emit('show-snackbar', {
          message: `Failed to send. ${error} `,
          actionText: 'Understood. :(',
        });
        this.isLoading = false;
      });

控制台输出:

12:38:51.888 Error: "Network Error"
    createError webpack-internal:///./node_modules/axios/lib/core/createError.js:16:15
    handleError webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:87:14
replybox.vue:62

编辑 3(1 月 6 日)

我在之前的编辑中意识到,在我从 postMessage 定义中删除 .then.catch 后,我省略了 error.request 的输出。如果我将 console.log(error.request); 重新添加到我的组件中调用的 .catch 块,这就是输出:

12:58:55.436
XMLHttpRequest
    mozAnon: false
    mozSystem: false
    onabort: null
    onerror: function handleError()
    onload: null
    onloadend: null
    onloadstart: null
    onprogress: null
    onreadystatechange: function handleLoad()
    ontimeout: function handleTimeout()
    readyState: 4
    response: ""
    responseText: ""
    responseType: ""
    responseURL: ""
    responseXML: null
    status: 0
    statusText: ""
    timeout: 100000
    upload: XMLHttpRequestUpload { onloadstart: null, onprogress: null, onabort: null, … }
    withCredentials: false
    <prototype>: XMLHttpRequestPrototype { open: open(), setRequestHeader: setRequestHeader(), send: send(), … }

编辑 4(2 月 6 日)

为了确认或排除我对 API 抽象层的实现,我直接在我的组件中调用了一个 Axios 实例:

const token = '*********************';
const axiosOptions = {
  baseURL: 'https://www.udemy.com/instructor-api/v1',
  timeout: 100000,
  headers: {
    Accept: '*/*',
    'Content-Type': 'application/json;charset=utf-8',
    Authorization: `Bearer ${token}`,
  },
};
const axiosInstance = axios.create(axiosOptions);
axiosInstance
.post(`/message-threadssss/${this.thread.id}/messages/`, {
  content: this.replyBody,
})
.then((response) => {
  this.isLoading = false;
  this.sentReply = response;
  this.replyBody = '';
  this.$root.$emit('reply-sent', {
    threadId: this.thread.id,
    sentReply: this.sentReply,
  });
})
.catch((error) => {
  console.log('Error obj: ', error);
  console.log('Request error obj: ', error.request);
  this.$root.$emit('show-snackbar', {
    message: `Failed to send. ${error} `,
    actionText: 'Understood. :(',
  });
  this.isLoading = false;
  this.axiosResult = error;
});

和以前一样,服务器 returned 了预期的 404,我组件中的 .catch 块捕获了错误。

尽管如此,捕获到的错误中缺少响应

13:25:45.783 Error obj:  Error: "Network Error"
    createError webpack-internal:///./node_modules/axios/lib/core/createError.js:16:15
    handleError webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:87:14
replybox.vue:79

13:25:45.786 Request error obj:  
XMLHttpRequest
    mozAnon: false
    mozSystem: false
    onabort: null
    onerror: function handleError()
    onload: null
    onloadend: null
    onloadstart: null
    onprogress: null
    onreadystatechange: function handleLoad()
    ontimeout: function handleTimeout()
    readyState: 4
    response: ""
    responseText: ""
    responseType: ""
    responseURL: ""
    responseXML: null
    status: 0
    statusText: ""
    timeout: 100000
    upload: XMLHttpRequestUpload { onloadstart: null, onprogress: null, onabort: null, … }
    withCredentials: false
    <prototype>: XMLHttpRequestPrototype { open: open(), setRequestHeader: setRequestHeader(), send: send(), … }
replybox.vue:80

我很确定您需要做的就是从 postMessage 函数中删除 .then.catch,您应该可以开始了

postMessage(messageThreadId, messageBody) {
  return axiosInstance
     .post(`/message-threadssss/${messageThreadId}/messages/`, {
        content: messageBody,
      })
}

这样,postMessage 返回一个承诺。当您实际调用 postMessage 时,您可以使用 .then.catch

所以,看起来的答案实际上是 API 我打电话的结果是没有提供正确的 CORS headers 错误响应(因此 CORS 只允许 2xx 响应)。

因此,Axios 无法访问响应。

目前我需要解决一个普遍的模棱两可的错误,但未来的解决方案取决于 API 为 CORS 提供成功和错误响应的开发人员。

非常感谢 Bergi 的帮助,最终让我找到了问题的根源。