Catch axios requests in different files / function

2019-07-27 07:34发布

问题:

I use HTTP requests to get data for my Vue.js application. I have one file called Api.js with the base axios instance:

export default () => {
  return axios.create({
    baseURL: apiURL,
    headers: {
      Authorization: `JWT ${store.state.token}`
    }
  })
}

than I have a file called service.js, which contains the functions for the different endpoints:

export default {
  status() {
    return Api().get('status/')
  }
}

In the .vue file I call the method like that.

created() {
  Service.status()
    .then(response => {
      // do something with the data
    })
    .catch(e => {
      // show exception
    })
}

Some exceptions should be handled in Api.js (for example: 401), some other exceptions should be handled in service.js and others in the .vue file. How can I do that?

回答1:

Disclaimer: I have created two small axios plugins to achieve this specific pattern easily.

axios-middleware

Simple axios HTTP middleware service to simplify hooking to HTTP requests made through Axios.

It uses axios interceptors as mentioned by acdcjunior but it abstracts the use of axios with a commonly known middleware pattern so your app doesn't need to know and deal with the interceptor syntax.

// import your API's axios instance
import http from './api';
import { Service } from 'axios-middleware';

// Create a new service instance
const service = new Service(http);

// We're good to go!
export default service;

You can then use this middleware service to register different middlewares anywhere in your app. A middleware can be as simple as an object or a reusable, easily testable class.

import i18n from './services/i18n';
import toast from './services/toast';
import service from './services/middleware';
import { ApiErrorMiddleware, OtherMiddleware } from './middlewares';


// Then register your middleware instances.
service.register([
    // Middleware class instance
    new ApiErrorMiddleware(i18n, toast),
    new OtherMiddleware(),
    // or a simple object
    {
        onRequest() {
            // handle the request
        },
        onResponseError(error) {
            // handle the response error
        }
    }
]);

Where the ApiErrorMiddleware would be a simple class with the sole responsibility of showing toast messages on error.

export default class ApiErrorMiddleware {
    /**
     * @param {VueI18n} i18n instance
     * @param {Object} toast message service
     */
    constructor(i18n, toast) {
        this.toast = toast;
        this.i18n = i18n;
    }

    /**
     * @param {Object} error
     */
    onResponseError(error = {}) {
        const { response } = error;
        let key = 'errors.default';

        if (response && this.i18n.te(`errors.${response.status}`)) {
            key = `errors.${response.status}`;
        } else if (error.message === 'Network Error') {
            key = 'errors.network-error';
        } else {
            // TODO log unhandled errors
        }
        this.toast.error(this.i18n.t(key));
    }
}

axios-resource

Simple axios resource class to easily interact with a REST endpoint.

Define a resource class. Here, I added onError and onFetchError as examples for your use-case.

import Resource from 'axios-resource';

export default class UserResource extends Resource {
    static URL = 'user/{id}';

    // This calls `sync` in the background
    fetch() {
        return super.fetch.apply(this, arguments)
            .catch(err => this.onFetchError(err));
    }

    onFetchError(err) {
        // An error occurred while fetching this resource.
    }

    onError(err) {
        // An error occurred with this resource
    }

    // called for every actions (fetch, create, patch, delete)
    sync() {
        return super.sync.apply(this, arguments)
            .catch((err) => this.onError(err))
    }
}

Then, in api.js, create an instance.

import UserResource from './user';

const user = new UserResource();

// GET https://example.com/api/user/me
user.fetch('me')
    .then(({ data }) => {
        console.log('User data:', data);
    });

The error can be dealt with at every step.

  1. in the onFetchError of this specific resource
  2. in the onError of this resource
  3. in a middleware for the app.


回答2:

You should add axios interceptors:

Axios Interceptors

You can intercept requests or responses before they are handled by then or catch.

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });

Those can (should) be in your Api.js.