为啥我的 Axios 实例没有在捕获的错误中返回响应?

Posted

技术标签:

【中文标题】为啥我的 Axios 实例没有在捕获的错误中返回响应?【英文标题】:Why does my instance of Axios not return the response in a caught error?为什么我的 Axios 实例没有在捕获的错误中返回响应? 【发布时间】:2019-06-29 15:41:41 【问题描述】:

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

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

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

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

以下是从文件中获取的,尽管为了便于阅读,我已经删除了模块导出的所有函数,但其​​中一个函数除外(并且显然修改了 API 令牌)。端点 URI 包含“message-threadssss”——这是故意的,导致服务器返回 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() 块中的响应数据。

当服务器返回错误(本例中为 404)时,我希望捕获的错误包含 response 对象(案例 1)。

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

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

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

总而言之,我有点茫然。如何将服务器的响应获取到我的 Vue 组件中?

编辑(2 月 6 日)

我没有在我的初始帖子中包含所有有用的控制台输出,所以在这里。执行的 console.log() 行是案例 2 行(仅添加了一个控制台日志条目,不以“错误:”为前缀,如案例 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;
);

和以前一样,服务器返回了预期的 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

【问题讨论】:

请发送console.log(error) 以及您收到的消息。 由于the wrong .catch(error =&gt; error); 我怀疑您的catch 语句实际上是否被调用。如果是,那很可能是因为前面的 then 回调中的异常 .then(response =&gt; response.data) 完全没问题,如果你愿意,你应该保留它。 Axios 会抛出一个Error: "Network Error",但请求有 status = 0 而不是 404。可能是 CORS 问题,并且不允许您访问响应? 好吧。对我来说,这是一个多么容易被忽视的菜鸟错误。正是这样。返回 2xx 状态时,API 似乎只允许跨域请求。我错误地认为这是一揽子政策。最尴尬的部分是,如果我没有过滤我的控制台,我就会看到这个,这样我就可以专注于我认为是相关的输出。非常感谢您的帮助! 【参考方案1】:

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

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

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

【讨论】:

感谢您的回答艾萨克!我已根据您的建议更新了我的 postMessage 函数。然而,问题似乎仍然存在。我已经用做出改变的结果更新了我的问题。【参考方案2】:

因此,看起来答案实际上是我调用的 API 没有提供带有错误响应的正确 CORS 标头的结果(因此 CORS 仅允许用于 2xx 响应)。

因此,Axios 无法访问响应。

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

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

【讨论】:

以上是关于为啥我的 Axios 实例没有在捕获的错误中返回响应?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 axios .catch() 在 redux 中间件中捕获 javascript 错误?

为啥我使用 axios 获取数据时出现错误 500

为啥我的图像在这些奇怪的字符中回响?

为啥我的 Axios fetch 会出现 CORS 错误?

多次调用后没有 Access-Control-Allow-Origin 编辑 > Axios 网络错误捕获

Vue.js:vuex 操作中未捕获的承诺