简体   繁体   English

为什么我的 Axios 实例在捕获的错误中不返回响应?

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

I am currently working on a project to create a client for the Udemy instructor API.我目前正在开展一个项目,为 Udemy 讲师 API 创建客户端。

I've written the client in Vue, using Axios as my HTTP client.我已经用 Vue 编写了客户端,使用 Axios 作为我的 HTTP 客户端。

I have abstracted the different API requests into functions in an ES6-ified API wrapper library ( Udemy.js ) to allow them to be easily reused.我已将不同的 API 请求抽象为 ES6 化 API 包装器库 ( Udemy.js ) 中的函数,以允许它们轻松重用。

Udemy.js first initialises an instance of Axios, then exports API functions that use that instance as a base as promises. Udemy.js首先初始化一个 Axios 实例,然后将使用该实例作为基础的 API 函数作为 promise 导出。

Below is taken from the file, though I have removed all but one of the functions the module exports for ease of reading (and obviously redacted the API token).下面是从文件中截取的,尽管我已经删除了模块导出的所有功能,但为了便于阅读(并且显然编辑了 API 令牌)。 The endpoint URI contains "message-threadssss" — this is deliberate, to cause the server to return 404:端点 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; });

The request sends without a problem, and if the request is a success (ie 2xx), the Vue component is able to access the response data in the then() block.请求发送没有问题,如果请求成功(即 2xx),Vue 组件就可以访问then()块中的响应数据。

When the server returns an error (404 in this instance), I would expect the caught error to contain a response object (Case 1).当服务器返回错误(在本例中为 404)时,我希望捕获的错误包含response对象(案例 1)。

Instead though, no response object is returned with the error (Case 2), which prevents me from handling it correctly.但是,没有response对象返回错误(案例 2),这使我无法正确处理它。 This happens when the request does cause the server to respond with a 404 error:当请求确实导致服务器以 404 错误响应时,就会发生这种情况:

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

I've read that if Axios has interceptors applied to it, that can lead to this issue, but in this case, I've not applied any interceptors.我读过,如果 Axios 应用了拦截器,可能会导致这个问题,但在这种情况下,我没有应用任何拦截器。

All in all, I'm at a bit of a loss.总而言之,我有点不知所措。 How do I get the server's response into my Vue component?如何将服务器的响应发送到我的 Vue 组件中?

Edit (6th Feb)编辑(2月6日)

I didn't include the all-useful console output in my initial post, so here it is.我没有在我的初始帖子中包含所有有用的控制台输出,所以在这里。 The console.log() line executed is the Case 2 line (just one console log entry is added, not prefixed with "Error: ", as would be the case in Case 3):执行的 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

Edit 2 (6th Feb)编辑 2(2 月 6 日)

If I remove the then() and catch() from the postMessage() definition to look like this:如果我从postMessage()定义中删除then()catch()看起来像这样:

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

And then simplify the catch() block of the postMessage() call to just output the error object to look like this:然后简化postMessage()调用的catch()块以仅输出error对象,如下所示:

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

The console outputs:控制台输出:

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

Edit 3 (6th Jan)编辑 3(1 月 6 日)

I realised in my previous edit, I omitted the output of error.request after I'd removed .then and .catch from my postMessage definition.我在以前的编辑意识到,我省略了输出error.request我已经删除后.then.catch从我postMessage定义。 If I re-add console.log(error.request);如果我重新添加console.log(error.request); to the .catch block of the call in my component, this is the output:到我组件中调用的.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(), … }

Edit 4 (6th Feb)编辑 4(2 月 6 日)

To confirm or rule out my implementation of my API abstraction layer, I directly invoked an Axios instance in my component:为了确认或排除我对 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;
});

As before, the server returned the expected 404, and the .catch block in my component caught the error.和以前一样,服务器返回了预期的 404,我的组件中的.catch块捕获了错误。

As before though, the response was missing from the caught error和以前一样,捕获的错误中缺少响应

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

So, the answer it seems was actually the result of the API I was calling not serving the correct CORS headers with error responses (so CORS was only allowed for 2xx responses).所以,答案似乎实际上是我调用的 API 没有提供正确的 CORS 标头和错误响应的结果(因此 CORS 只允许用于 2xx 响应)。

Consequently, Axios was unable to access the response.因此,Axios 无法访问响应。

I'll need to work around a general ambiguous error for the moment, but solution going forwards lies with developers of the API serving CORS with both success and error responses.目前我需要解决一个一般性的模糊错误,但未来的解决方案取决于为 CORS 提供成功和错误响应的 API 的开发人员。

Many thanks to Bergi for their help, which ultimately led me to the cause of the issue.非常感谢Bergi的帮助,这最终让我找到了问题的根源。

I'm pretty sure all you need to do is remove the .then and .catch from your postMessage function and you should be good to go我敢肯定,所有你需要做的是去除.then.catchpostMessage功能,你应该是好去

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

This way, postMessage is returning a promise.这样, postMessage正在返回一个承诺。 And when you actually call postMessage you can use .then and .catch而当你实际调用postMessage可以使用.then.catch

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM