Vue SPA检索嵌套承诺中错误代码(不是200)上的状态代码

时间:2019-08-25 15:50:17

标签: javascript vue.js axios

在我的VUE组件中,我使用此异步方法从API提取数据:

组件

methods: {
  async fetch() {
    // console.log("##### WAIT ####");
    const { data } = await staffRepository.getItems(this.teamId)
    // console.log("##### END WAIT ####");
    this.staffs = data
  },
},

如您所见,我使用一个自定义存储库来拥有一个axios代码,该存储库已导入到我以前的组件中。

staffRepository

export default {
  getItems(nationId) {
    return Repository.get(`page/${nationId}`)
  },
}

最后是具有axios代码的主存储库:

存储库

import axios from 'axios/index'
const baseDomain = 'https://my end point'
const baseURL = `${baseDomain}`
...
const headers = {
  'X-CSRF-TOKEN': token,
  //  'Access-Control-Allow-Origin': '*', // IF you ADD it add 'allowedHeaders' to ai server config/cors.php
  'X-Requested-With': 'XMLHttpRequest',
  'Content-Type': 'application/json',
  Authorization: `Bearer ${jwtoken}`,
}
export default axios.create({
  baseURL,
  withCredentials: withCredentials,
  headers: headers,
})

当jwtoken是有效且不是EXIPRED令牌时,此代码非常有用。

问题是令牌过期或找不到令牌,而我的laravel 5.8 API返回状态代码401(或其他状态)。

  

获取https://api.endpoint 401(未经授权)

一个好的解决方案可以在具有get方法的 staffRepository 中捕获状态代码。

MySolution :(不起作用)

getItems(nationId) {
  return Repository.get(`page/${nationId}`)
    .then(response => {
      console.log(response)
    })
    .catch(error => {
      console.log(error.response.status) // <-- it works!
    })
},

这可能很好,因为在错误情况下,控制台中的错误为 401

但是我不能使用这种解决方案,因为我有2个嵌套的promise:这个诺言和将异步fetch()放入组件中。

如何在仍使用存储库环境的情况下修复它?

2 个答案:

答案 0 :(得分:1)

我建议您在组件中使用返回的Promise,以使事情更明确:

methods: {
  fetch() {
    let data = null

    staffRepository
      .getItems(this.teamId)
      .then(data => {
        // do something with data
        this.staffs = data
      })
      .catch(e => {
        // do something with error, or tell the user
      })
  },
},

编辑-这将完全正常,因为如果您使用axios,则存储库中的方法将默认返回一个Promise。

尝试一下:API代码,其中HTTP是axios实例

export const get = (path: string): Promise<any> => {
  return new Promise((resolve, reject) => {
    HTTP.get(`${path}`)
      .then((response) => {
        resolve(response);
      })
      .catch((error) => {
        reject(handleError(error));
      });
  });
};

// ***** Handle errors *****/

export function handleError(error) {
  if (error.response) {
    const status = error.response.status;

    switch (status) {
      case 400:
        // do something
        break;
      case 401:
        // do something, maybe log user out
        break;
      case 403:
        break;
      case 500:
        // server error...
        break;
      default:
      // handle normal errors here
    }
  }
  return error; // Return the error message, or whatever you want to your components/vue files
}

答案 1 :(得分:1)

最佳实践解决方案是使用axios的拦截器:

import axios from "axios";
import Cookies from "js-cookie";

export default (options = {}) => {
  let client = options.client || axios.create({ baseURL: process.env.baseUrl });
  let token = options.token || Cookies.get("token");
  let refreshToken = options.refreshToken || Cookies.get("refreshToken");
  let refreshRequest = null;

  client.interceptors.request.use(
    config => {
      if (!token) {
        return config;
      }

      const newConfig = {
        headers: {},
        ...config
      };

      newConfig.headers.Authorization = `Bearer ${token}`;
      return newConfig;
    },
    e => Promise.reject(e)
  );

  client.interceptors.response.use(
    r => r,
    async error => {
      if (
        !refreshToken ||
        error.response.status !== 401 ||
        error.config.retry
      ) {
        throw error;
      }

      if (!refreshRequest) {
        refreshRequest = client.post("/auth/refresh", {
          refreshToken
        });
      }

      const { data } = await refreshRequest;
      const { token: _token, refreshToken: _refreshToken } = data.content;

      token = _token;
      Cookies.set("token", token);

      refreshRequest = _refreshToken;
      Cookies.set("refreshToken", _refreshToken);

      const newRequest = {
        ...error.config,
        retry: true
      };

      return client(newRequest);
    }
  );

  return client;
};

看看client.interceptors.response.use。另外,您应该有一个refreshToken。我们正在拦截401响应并发送发布请求以刷新令牌,然后等待新的 fresh 令牌并重新发送之前的请求。这是一种非常优雅且经过测试的解决方案,可以满足我公司的需求,并且也可能会满足您的需求。

要发送请求,请使用:

import api from './api'

async function me() {
  try {
    const res = await api().get('/auth/me')
    // api().post('/auth/login', body) <--- POST

    if (res.status === 200) { alert('success') }
  } catch(e) {
    // do whatever you want with the error
  }
}
  

刷新令牌:刷新令牌用于生成新访问   令牌。通常,如果访问令牌具有失效日期,则一旦   过期,用户将必须再次进行身份验证才能获得访问权限   令牌。使用刷新令牌,可以跳过此步骤,并且可以请求   到API会获得一个新的访问令牌,该令牌允许用户继续   访问应用程序资源。