最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why does my instance of Axios not return the response in a caught error? - Stack Overflow

programmeradmin3浏览0评论

I am currently working on a project to create a client for the Udemy instructor API.

I've written the client in Vue, using Axios as my HTTP client.

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.

Udemy.js first initialises an instance of Axios, then exports API functions that use that instance as a base as promises.

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). The endpoint URI contains "message-threadssss" — this is deliberate, to cause the server to return 404:

import axios from 'axios';

const token = '***************************';
const axiosOptions = {
  baseURL: '',
  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);
  },
}

I am currently working on a project to create a client for the Udemy instructor API.

I've written the client in Vue, using Axios as my HTTP client.

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.

Udemy.js first initialises an instance of Axios, then exports API functions that use that instance as a base as promises.

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). The endpoint URI contains "message-threadssss" — this is deliberate, to cause the server to return 404:

import axios from 'axios';

const token = '***************************';
const axiosOptions = {
  baseURL: 'https://www.udemy./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 (i.e. 2xx), the Vue ponent is able to access the response data in the then() block.

When the server returns an error (404 in this instance), I would expect the caught error to contain a response object (Case 1).

Instead though, no response object is returned with the error (Case 2), which prevents me from handling it correctly. This happens when the request does cause the server to respond with a 404 error:

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.

All in all, I'm at a bit of a loss. How do I get the server's response into my Vue ponent?

Edit (6th Feb)

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):

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)

If I remove the then() and catch() from the postMessage() definition to look like this:

 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:

    .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)

I realised in my previous edit, I omitted the output of error.request after I'd removed .then and .catch from my postMessage definition. If I re-add console.log(error.request); to the .catch block of the call in my ponent, this is the output:

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)

To confirm or rule out my implementation of my API abstraction layer, I directly invoked an Axios instance in my ponent:

const token = '*********************';
const axiosOptions = {
  baseURL: 'https://www.udemy./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 ponent caught the error.

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
Share Improve this question edited Feb 6, 2019 at 13:33 Andrew Dunn asked Feb 5, 2019 at 18:31 Andrew DunnAndrew Dunn 7551 gold badge7 silver badges21 bronze badges 13
  • Please do a console.log(error) and what message you are getting. – Bergi Commented Feb 5, 2019 at 18:54
  • 3 Due to the wrong .catch(error => error); I doubt that your catch statement actually gets called at all. And if it is, that's likely because of an exception in the preceding then callback – Bergi Commented Feb 5, 2019 at 18:56
  • 1 The .then(response => response.data) is totally fine, you should keep it if you want it. – Bergi Commented Feb 6, 2019 at 13:27
  • 1 Axios throws a Error: "Network Error" though, and the request has status = 0 not 404. Maybe a CORS problem, and you're not allowed to access the response? – Bergi Commented Feb 6, 2019 at 13:40
  • 1 Well. What a rookie error for me to overlook. It was exactly that. It seems the API only allows cross-origin requests when returning a 2xx status. I had wrong assumed this was a blanket policy. The most embarrassing part is that I'd have seen this if I'd not have filtered my console so I could focus on what I thought to be the relevent output. Thanks so much for your help! – Andrew Dunn Commented Feb 6, 2019 at 13:53
 |  Show 8 more ments

2 Answers 2

Reset to default 6

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).

Consequently, Axios was unable to access the response.

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.

Many thanks to Bergi for their help, which ultimately led me to the cause of the issue.

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

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

This way, postMessage is returning a promise. And when you actually call postMessage you can use .then and .catch

发布评论

评论列表(0)

  1. 暂无评论