我有一个react/redux应用程序,从api服务器获取一个令牌。在用户进行身份验证之后,我希望所有axios请求都具有该令牌作为授权标头,而不必手动将其附加到操作中的每个请求。我是相当新的反应/redux,我不确定最好的方法,我没有找到任何高质量的谷歌。

这是我的redux设置:

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

export function loginUser(props) {
  const url = `https://api.mydomain.com/login/`;
  const { email, password } = props;
  const request = axios.post(url, { email, password });

  return {
    type: LOGIN_USER,
    payload: request
  };
}

export function fetchPages() {
  /* here is where I'd like the header to be attached automatically if the user
     has logged in */ 
  const request = axios.get(PAGES_URL);

  return {
    type: FETCH_PAGES,
    payload: request
  };
}

// reducers.js
const initialState = {
  isAuthenticated: false,
  token: null
};

export default (state = initialState, action) => {
  switch(action.type) {
    case LOGIN_USER:
      // here is where I believe I should be attaching the header to all axios requests.
      return {
        token: action.payload.data.key,
        isAuthenticated: true
      };
    case LOGOUT_USER:
      // i would remove the header from all axios requests here.
      return initialState;
    default:
      return state;
  }
}

我的令牌存储在redux store下的state.session.token。

我有点不知道该怎么做。我尝试在根目录的文件中创建一个axios实例,并更新/导入该实例,而不是从node_modules中更新/导入,但当状态发生变化时,它没有附加头文件。任何反馈/想法都非常感谢,谢谢。


当前回答

创建axios实例:

// Default config options
  const defaultOptions = {
    baseURL: <CHANGE-TO-URL>,
    headers: {
      'Content-Type': 'application/json',
    },
  };

  // Create instance
  let instance = axios.create(defaultOptions);

  // Set the AUTH token for any request
  instance.interceptors.request.use(function (config) {
    const token = localStorage.getItem('token');
    config.headers.Authorization =  token ? `Bearer ${token}` : '';
    return config;
  });

然后,对于任何请求,将从localStorage中选择令牌,并将其添加到请求头中。

我在整个应用程序中使用相同的实例:

import axios from 'axios';

const fetchClient = () => {
  const defaultOptions = {
    baseURL: process.env.REACT_APP_API_PATH,
    method: 'get',
    headers: {
      'Content-Type': 'application/json',
    },
  };

  // Create instance
  let instance = axios.create(defaultOptions);

  // Set the AUTH token for any request
  instance.interceptors.request.use(function (config) {
    const token = localStorage.getItem('token');
    config.headers.Authorization =  token ? `Bearer ${token}` : '';
    return config;
  });

  return instance;
};

export default fetchClient();

其他回答

类似地,我们有一个函数来从调用中设置或删除令牌,如下所示:

import axios from 'axios';

export default function setAuthToken(token) {
  axios.defaults.headers.common['Authorization'] = '';
  delete axios.defaults.headers.common['Authorization'];

  if (token) {
    axios.defaults.headers.common['Authorization'] = `${token}`;
  }
}

我们总是在初始化时清除现有的令牌,然后建立接收到的令牌。

对我来说,最好的解决方案是创建一个客户端服务,您将实例化您的令牌,并使用它来包装axios。

import axios from 'axios';

const client = (token = null) => {
    const defaultOptions = {
        headers: {
            Authorization: token ? `Token ${token}` : '',
        },
    };

    return {
        get: (url, options = {}) => axios.get(url, { ...defaultOptions, ...options }),
        post: (url, data, options = {}) => axios.post(url, data, { ...defaultOptions, ...options }),
        put: (url, data, options = {}) => axios.put(url, data, { ...defaultOptions, ...options }),
        delete: (url, options = {}) => axios.delete(url, { ...defaultOptions, ...options }),
    };
};

const request = client('MY SECRET TOKEN');

request.get(PAGES_URL);

在这个客户机中,您还可以根据需要从localStorage / cookie检索令牌。

有时会遇到这样的情况,使用axios发出的一些请求被指向不接受授权标头的端点。因此,只在允许的域上设置授权头的另一种方法如下例所示。将以下函数放置在每次React应用程序运行时执行的任何文件中,例如在路由文件中。

export default () => {
    axios.interceptors.request.use(function (requestConfig) {
        if (requestConfig.url.indexOf(<ALLOWED_DOMAIN>) > -1) {
            const token = localStorage.token;
            requestConfig.headers['Authorization'] = `Bearer ${token}`;
        }

        return requestConfig;
    }, function (error) {
        return Promise.reject(error);
    });

}
export const authHandler = (config) => {
  const authRegex = /^\/apiregex/;

  if (!authRegex.test(config.url)) {
    return store.fetchToken().then((token) => {
      Object.assign(config.headers.common, { Authorization: `Bearer ${token}` });
      return Promise.resolve(config);
    });
  }
  return Promise.resolve(config);
};

axios.interceptors.request.use(authHandler);

在尝试实现类似的东西时遇到了一些陷阱,基于这些答案,这是我想出的。我遇到的问题是:

If using axios for the request to get a token in your store, you need to detect the path before adding the header. If you don't, it will try to add the header to that call as well and get into a circular path issue. The inverse of adding regex to detect the other calls would also work If the store is returning a promise, you need to return the call to the store to resolve the promise in the authHandler function. Async/Await functionality would make this easier/more obvious If the call for the auth token fails or is the call to get the token, you still want to resolve a promise with the config

有多种方法可以实现这一点。在这里,我解释了两种最常见的方法。

1. 可以使用axios拦截器拦截任何请求并添加授权头。

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    const token = store.getState().session.token;
    config.headers.Authorization =  token;
     
    return config;
});

2. 从axios的文档中,您可以看到有一种机制允许您设置将随您发出的每个请求一起发送的默认头。

axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;

在你的例子中:

axios.defaults.headers.common['Authorization'] = store.getState().session.token;

如果您愿意,可以创建一个自执行函数,当令牌出现在存储中时,该函数将设置授权头本身。

(function() {
     String token = store.getState().session.token;
     if (token) {
         axios.defaults.headers.common['Authorization'] = token;
     } else {
         axios.defaults.headers.common['Authorization'] = null;
         /*if setting null does not remove `Authorization` header then try     
           delete axios.defaults.headers.common['Authorization'];
         */
     }
})();

现在您不再需要手动将令牌附加到每个请求。你可以把上面的函数放在保证每次执行的文件中(例如:包含路由的文件)。