开发者问题收集

当不同的组件使用 axiosinstance 调用 API 时,在哪里添加/删除拦截器

2022-02-01
2195

我在应用程序启动时初始化了 axios 实例。在 Login.js 下,我能够获取令牌,并希望使用拦截器将其添加到标头,用于大多数后续 api 调用,例如在 AddSampleTable.js 下使用时。(其中一些也需要在没有授权标头的情况下使用,例如 ForgotPassword.js)

目前,我必须对每个组件中的每个 api 调用执行此操作。我当前的代码如下

axios.js

import axios from 'axios';

 const baseURL = process.env.REACT_APP_BASE_URL;

 let headers = {};

//this never executes since we havent logged in yet
  
//if(localStorage.token) {
  //headers.Authorization = `Bearer ${localStorage.token}`; 
//}
const token = localStorage.getItem('token');

const axiosInstance = axios.create({

    baseURL: baseURL,
    headers: {'Authorization': token? `Bearer ${token}`: null},
});    
  
export default axiosInstance;

Login.js

import axiosInstance from '../../helpers/axios';

  const printValues = e =>{
  e.preventDefault();
 axiosInstance.post('/auth', data)
 .then(res =>{
  console.log("writing token");
  dispatch(jwtTokenRecieved(res.data.token));
  localStorage.setItem('token',res.data.token);

  const config = {
    headers:{
      Authorization:'Bearer '+res.data.token
    }
  }
  axiosInstance.get('/User/GetUserByID/0', config)
.then(res =>{
    dispatch(isLoggedUser(res.data));
  })
  .catch(err =>{
    console.log(err);
  })

AddSampleTable.js

这是我想要使用实例的地方,默认情况下令牌应该存在,但目前我正在从 localstorage 中提取每个 api 调用

import axiosInstance from '../../helpers/axios';
export default function AddSamplesTable(){
const jwtToken = useSelector(state => state?.token?.data || '');

const retrieveSampleData = () =>{

const config = {
  headers:{
    Authorization:'Bearer '+ jwtToken,
    'Content-Type': 'application/json'
  }
}
axiosInstance.get('/batch/'+CoCData.id, config) 
    .then(function (response) {
      setSamples(response.data.samples);
    })
    .catch(function (error) {
      console.log(error);
    });
} 


}

注意我还使用 Reducer 和操作将令牌设置到 localStorage 中,如您所见(除了通过 setItem 保存它)

dispatch(jwtTokenRecieved(res.data.token));

更新:我曾尝试在创建函数后在 axios.js 中使用拦截器,如下所示

axiosInstance.interceptors.request.use(
config => {
console.log(config)
  const token = JSON.parse(localStorage.getItem('token'));
  config.headers.Authorization =  token ? `Bearer ${token}`: null;

  return config;
    }
);

但是当新用户登录时,现有令牌值不会使用新令牌进行更新,我

获取

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'status')

3个回答

对安全 API 和访客 API 使用 不同的 Axios 实例

请按照以下步骤操作:

  1. 为安全和访客 API 定义 baseURL
  2. 创建不同的 Axios 实例
  3. 为安全 API Axios 实例定义请求、响应和错误处理程序
  4. 使用请求处理程序,将令牌放入请求标头中
  5. 将上述处理程序作为拦截器绑定到安全 Axios 实例
  6. 导出两个 axios 实例并根据需要使用
  7. 在注销或使用计时器时清除令牌

axios.js

// /userLibrary/axios.js
import axios from 'axios';

// Step 1
const baseURL = process.env.REACT_APP_BASE_URL;

// Step 2
const guestAxios = axios.create({ baseURL })
const secureAxios = axios.create({ baseURL })

// Step 3: Define handlers
const errorHandler = ()=>{
   ...
}

const secureAPIRequest= (config)=>{
   
    // Step 4: Put token in header
    const token = JSON.parse(localStorage.getItem('token'));
    config.headers.Authorization =  token ? `Bearer ${token}`: null;
    return config
}

const secureAPIResponse= ()=>{
   ...
}

// Step 5: Add interceptors for secure axios instance
secureAxios.interceptors.request.use(secureAPIRequest, errorHandler)
secureAxios.interceptors.response.use(secureAPIResponse, errorHandler)

// Step 6
export {
    guestAxios,
    secureAxios
}

axios.js

// /userLibrary/axios.js
import axios from 'axios';

// Step 1
const baseURL = process.env.REACT_APP_BASE_URL;

// Step 2
const guestAxios = axios.create({ baseURL })
const secureAxios = axios.create({ baseURL })

// Step 3: Define handlers
const errorHandler = ()=>{
   ...
}

const secureAPIRequest= (config)=>{
   
    // Step 4: Put token in header
    const token = JSON.parse(localStorage.getItem('token'));
    config.headers.Authorization =  token ? `Bearer ${token}`: null;
    return config
}

const secureAPIResponse= ()=>{
   ...
}

// Step 5: Add interceptors for secure axios instance
secureAxios.interceptors.request.use(secureAPIRequest, errorHandler)
secureAxios.interceptors.response.use(secureAPIResponse, errorHandler)

// Step 6
export {
    guestAxios,
    secureAxios
}
Login.js
import { guestAxios } from '/userLibrary/axios.js'
...

AddSampleTable.js 的示例代码

import { secureAxios } from '/userLibrary/axios.js'
...
Alpesh Patil
2022-02-10

这是一个固执己见的答案,但我主张将 React 应用的代码分成两个区域:

视图

务必使用 React 样式,其中每个源文件仅包含函数,并遵循 React 最佳实践。

常规代码/API 调用

类通常在这里工作得更好,可以将代码和数据封装在一个实例中。在需要时将 ApiClientLoginClient 实例作为 props 传递。来自视图的 API 调用将始终是一行干净的代码,没有代码重复。

示例代码

请参阅 此 Curity 代码示例 。还要注意,安全相关数据仅存储在内存中,从安全角度来看,这通常更好。本地存储中的令牌具有更大的 XSS 风险。

Gary Archer
2022-02-09

我建议您阅读 react 和 jwt 的完整示例 然后您可以使用 react-redux-jwt 检查差异>

在 redux 中存储令牌时要小心,每次刷新页面时您都会重新启动 redux 存储,如果您将其作为事实来源,则可能需要每次都重新登录用户(通常是不想要的行为)。 这就是为什么您可以使用 sessionStorage 或 localStorage 或 cookies

sessionStorage(顾名思义)仅在浏览器会话期间可用(并在选项卡或窗口关闭时被删除) - 但它在页面重新加载后仍然存在

localStorage 即使在关闭浏览器并再次打开时也可用,您必须手动或使用 JS 将其删除。

cookies 是另一种选择,它体积更小,在客户端和服务器之间共享,它们在所有请求中传播,它提供了一些过期时间,可以签名或加密。

guiwme5
2022-02-04