你如何使用 axios 拦截器?

IT技术 javascript axios
2021-03-08 23:10:20

我看过 axios 文档,但它所说的只是

// 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);
});

还有很多教程只展示了这段代码,但我很困惑它的用途,有人可以给我一个简单的例子来遵循。

5个回答

简单来说,它更像是每个 HTTP 操作的检查点。进行的每个 API 调用都通过此拦截器传递。

那么,为什么有两个拦截器?

API 调用由两部分组成,请求和响应。由于它的行为类似于检查点,因此请求和响应具有单独的拦截器。

一些请求拦截器用例 -

假设您想在提出请求之前检查您的凭据是否有效。因此,您可以在拦截器级别检查您的凭据是否有效,而不是实际进行 API 调用。

假设您需要为每个发出的请求附加一个令牌,而不是在每次 Axios 调用时复制令牌添加逻辑,您可以创建一个拦截器,为每个发出的请求附加一个令牌。

一些响应拦截器用例 -

假设您收到一个响应,并根据 API 响应判断您要推断用户已登录。因此,在响应拦截器中,您可以初始化一个处理用户登录状态的类,并在响应对象上相应地更新它你收到。

假设您已请求一些具有有效 API 凭证的 API,但您没有访问数据的有效角色。因此,您可以触发来自响应拦截器的警报,提示用户不被允许。通过这种方式,您将免于未经授权的 API 错误处理,而您必须对您发出的每个 Axios 请求执行这些错误处理。

下面是一些代码示例

请求拦截器

  • 可以通过执行(在这种情况下,通过检查环境变量)来打印 axios 的配置对象(如果需要):

    const DEBUG = process.env.NODE_ENV === "development";
    
    axios.interceptors.request.use((config) => {
        /** In dev, intercepts request and logs it into console for dev */
        if (DEBUG) { console.info("✉️ ", config); }
        return config;
    }, (error) => {
        if (DEBUG) { console.error("✉️ ", error); }
        return Promise.reject(error);
    });
    
  • 如果想检查传递了哪些标头/添加更多通用标头,它在config.headers对象中可用例如:

    axios.interceptors.request.use((config) => {
        config.headers.genericKey = "someGenericValue";
        return config;
    }, (error) => {
        return Promise.reject(error);
    });
    
  • 如果是GET请求,则可以在config.paramsobject 中找到正在发送的查询参数

响应拦截器

  • 您甚至可以选择在拦截器级别解析 API 响应,并将解析后的响应而不是原始响应向下传递。如果在多个地方以相同的方式使用 API,它可能会节省您一次又一次地编写解析逻辑的时间。一种方法是在 中传递一个额外的参数,api-request并在响应拦截器中使用相同的参数来执行您的操作。例如:

    //Assume we pass an extra parameter "parse: true" 
    axios.get("/city-list", { parse: true });
    

    有一次,在响应拦截器中,我们可以像这样使用它:

    axios.interceptors.response.use((response) => {
        if (response.config.parse) {
            //perform the manipulation here and change the response object
        }
        return response;
    }, (error) => {
        return Promise.reject(error.message);
    });
    

    因此,在这种情况下,只要 中有parse对象response.config,操作就会完成,对于其余情况,它将按原样工作。

  • 您甚至可以查看到达的HTTP代码,然后做出决定。例如:

    axios.interceptors.response.use((response) => {
        if(response.status === 401) {
             alert("You are not authorized");
        }
        return response;
    }, (error) => {
        if (error.response && error.response.data) {
            return Promise.reject(error.response.data);
        }
        return Promise.reject(error.message);
    });
    
@JamesPoulose 您可以分离出您的服务层,并将拦截器置于该服务层的底部,例如services/index.js. 所以前端应用程序停留在一个地方,服务保持独立且与平台无关:D
2021-04-18 23:10:20
@JamesPoulose 我通过在“src”文件夹下创建一个“services”文件夹解决了这个问题。在这个目录中,我将创建一个“request.js”或任何自定义名称来编写拦截器。
2021-04-30 23:10:20
你在哪里编写这些拦截器——在每个组件上还是在整个应用程序的全局/中心位置?
2021-05-05 23:10:20
@AseemUpadhyay我把这个代码services/index.jsexport default axios,然后import axios from "services/index.js",无论我用爱可信。但是我看到互联网上的其他人为 axios 创建了一个包装器……您个人使用哪种方法?谢谢
2021-05-13 23:10:20
@JamesPoulose 它应该位于中心位置,因为它的行为非常简单。它也可以在组件级别编写,但这通常会导致代码重复。这不明智
2021-05-15 23:10:20

例如,如果您想捕捉从发送请求到收到响应的时间,您可以使用此代码:

const axios = require("axios");

(async () => {
  axios.interceptors.request.use(
    function (req) {
      req.time = { startTime: new Date() };
      return req;
    },
    (err) => {
      return Promise.reject(err);
    }
  );

  axios.interceptors.response.use(
    function (res) {
      res.config.time.endTime = new Date();
      res.duration =
        res.config.time.endTime - res.config.time.startTime;
      return res;
    },
    (err) => {
      return Promise.reject(err);
    }
  );

  axios
    .get("http://localhost:3000")
    .then((res) => {
      console.log(res.duration)
    })
    .catch((err) => {
      console.log(err);
    });
})();
我赞成你的回答,因为你在一个地方同时展示了拦截器和实际调用。我很难把两者放在一起。
2021-04-28 23:10:20

它就像一个中间件,基本上它被添加到任何请求(GET、POST、PUT、DELETE)或任何响应(您从服务器获得的响应)上。它通常用于涉及授权的情况。

看看这个:Axios拦截器和异步登录

这是另一篇关于此的文章,有一个不同的例子:https : //medium.com/@danielalvidrez/handling-error-responses-with-grace-b6fd3c5886f0

因此,其中一个示例的要点是您可以使用拦截器来检测您的授权令牌是否已过期(例如,如果您收到 403)并重定向页面。

这个怎么样。您创建一个新的 Axios 实例并为其附加一个拦截器。然后你可以在你的应用程序的任何地方使用这个拦截器

export const axiosAuth = axios.create()

//we intercept every requests 
axiosAuth.interceptors.request.use(async function(config){
    //anything you want to attach to the requests such as token 
    return config;
}, error => {
    return Promise.reject(error)
})


//we intercept every response
axiosAuth.interceptors.request.use(async function(config){
    
    return config;
}, error => {
//check for authentication or anything like that
    return Promise.reject(error)
})

然后你使用你使用axiosAuth的相同方式axios

我会给你更实际的用例,我在现实世界的项目中使用过。我通常使用request interceptor与令牌相关的人员 ( accessToken, refreshToken),例如,令牌是否未过期,如果是,则使用 refreshToken 更新它并保持所有其他调用,直到它解决为止。但我最喜欢的是 axios response interceptors,您可以在其中放置您的应用程序全局错误处理逻辑,如下所示:

httpClient.interceptors.response.use(
  (response: AxiosResponse) => {
    // Any status code that lie within the range of 2xx cause this function to trigger
    return response.data;
  },
  (err: AxiosError) => {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    const status = err.response?.status || 500;
    // we can handle global errors here
    switch (status) {
      // authentication (token related issues)
      case 401: {
        return Promise.reject(new APIError(err.message, 409));
      }

      // forbidden (permission related issues)
      case 403: {
        return Promise.reject(new APIError(err.message, 409));
      }

      // bad request
      case 400: {
        return Promise.reject(new APIError(err.message, 400));
      }

      // not found
      case 404: {
        return Promise.reject(new APIError(err.message, 404));
      }

      // conflict
      case 409: {
        return Promise.reject(new APIError(err.message, 409));
      }

      // unprocessable
      case 422: {
        return Promise.reject(new APIError(err.message, 422));
      }

      // generic api error (server related) unexpected
      default: {
        return Promise.reject(new APIError(err.message, 500));
      }
    }
  }
);