如何使用 Axios 拦截器?

我看过 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);
});

也有很多教程只显示这个代码,但我很困惑它是用来,可以有人请给我简单的例子来遵循。

214089 次浏览

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

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

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

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

简单来说,它更像是每个 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.params对象中找到正在发送的查询参数。

响应拦截器

  • 您甚至可以在拦截器级别上解析 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);
    });
    

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

  • 你甚至可以查看到达的 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);
    });
    

例如,如果您希望捕捉从请求发送到您收到响应的时刻所花费的时间,您可以使用这段代码:

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

这样吧。您将创建一个新的 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)
})

然后像使用 axios一样使用 axiosAuth

我将给你更多的实际用例,我在我的现实世界项目中使用。我通常使用 request interceptor来处理令牌相关的人员(accessTokenrefreshToken) ,例如,令牌是否过期,如果过期了,那么使用 refshToken 更新令牌,并保持所有其他调用,直到令牌过期。但我最喜欢的是 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));
}
}
}
);

这是我以前在我的项目中做的方式。代码片段指出如何在 Axios 拦截器中使用访问和刷新令牌,并将帮助实现刷新令牌功能。

const API_URL =
process.env.NODE_ENV === 'development'
? 'http://localhost:8080/admin/api'
: '/admin-app/admin/api';


const Service = axios.create({
baseURL: API_URL,
headers: {
Accept: 'application/json',
},
});


Service.interceptors.request.use(
config => {
const accessToken = localStorage.getItem('accessToken');
if (accessToken) {
config.headers.common = { Authorization: `Bearer ${accessToken}` };
}
return config;
},
error => {
Promise.reject(error.response || error.message);
}
);


Service.interceptors.response.use(
response => {
return response;
},
error => {
let originalRequest = error.config;
let refreshToken = localStorage.getItem('refreshToken');
const username = EmailDecoder(); // decode email from jwt token subject
if (
refreshToken &&
error.response.status === 403 &&
!originalRequest._retry &&
username
) {
originalRequest._retry = true;
return axios
.post(`${API_URL}/authentication/refresh`, {
refreshToken: refreshToken,
username,
})
.then(res => {
if (res.status === 200) {
localStorage.setItem(
'accessToken',
res.data.accessToken
);
localStorage.setItem(
'refreshToken',
res.data.refreshToken
);


originalRequest.headers[
'Authorization'
] = `Bearer ${res.data.accessToken}`;


return axios(originalRequest);
}
})
.catch(() => {
localStorage.clear();
location.reload();
});
}
return Promise.reject(error.response || error.message);
}
);


export default Service;

我已经以下列方式实现了

HttpConfig.js

import axios from 'axios'
import { baseURL } from '../utils/config'
import { SetupInterceptors } from './SetupInterceptors'




const http = axios.create({
baseURL: baseURL
})


SetupInterceptors(http)


export default http

SetupInterceptors.js

import { baseURL } from '../utils/config'




export const SetupInterceptors = http => {


http.interceptors.request.use(
config => {
config.headers['token'] = `${localStorage.getItem('token')}`
config.headers['content-type'] = 'application/json'
return config
},
error => {
return Promise.reject(error)
}
)


http.interceptors.response.use(function(response) {
return response
}, function (error) {
const status = error?.response?.status || 0
const resBaseURL = error?.response?.config?.baseURL
if (resBaseURL === baseURL && status === 401) {
if (localStorage.getItem('token')) {
localStorage.clear()
window.location.assign('/')
return Promise.reject(error)
} else {
return Promise.reject(error)
}
}
return Promise.reject(error)
})
}


export default SetupInterceptors

参考资料: 链接