【问题标题】:Dynamically changing content-type in axios reactjs在axios reactjs中动态改变内容类型
【发布时间】:2021-09-08 16:55:35
【问题描述】:

所以这是我的问题。 我在我的项目中使用 JWT 身份验证,并且我的 react 项目中有一个 axiosInstance 设置。我还有一个 axiosInstance 的拦截器,它负责在需要时拦截和刷新令牌。

const axiosInstance = axios.create({
 ​baseURL: baseURL,
 ​timeout: 360000,
 ​transformRequest: [
   ​function (data, headers) {
     ​const accessToken = window.localStorage.getItem('access_token');
     ​if (accessToken) {
       ​headers['Authorization'] = `Bearer ${accessToken}`;
     ​} else {
       ​delete headers.Authorization;
     ​}

     ​return JSON.stringify(data);
   ​},
 ​],
 ​headers: {
   ​'Content-Type': 'application/json',
   ​accept: 'application/json',
 ​},
});


axiosInstance.interceptors.response.use(
  (response) => {
    return response;
  },
  async function (error) {
    const originalRequest = error.config;

    console.log(
      'Caught the error response. Here is your request  ',
      originalRequest,
    );

    // case 1: No error specified Most likely to be server error

    if (typeof error.response === 'undefined') {
      //  Uncomment this later
      alert('Server error occured');

      return Promise.reject(error);
    }

    //  case 2: Tried to refresh the token but it is expired. So ask user to login again

    if (
      error.response.status === 401 &&
      originalRequest.url === baseURL + 'auth/api/token/refresh/'
    ) {
      store.dispatch(setLoginFalse());
      return Promise.reject(error);
    }

    // Case 3: Got 401 Unauthorized error. There are different possiblities
    console.log('Error message in axios = ', error.response.data);
    if (
      error.response.status === 401 &&
      error.response.statusText === 'Unauthorized'
    ) {
      const refreshToken = localStorage.getItem('refresh_token');
      console.log('Refresh token = ', refreshToken);

      // See if refresh token exists
      // Some times undefined gets written in place of refresh token.
      // To avoid that we check if refreshToken !== "undefined". This bug is still unknown need to do more research on this

      if (refreshToken !== undefined && refreshToken !== 'undefined') {
        console.log(typeof refreshToken == 'undefined');
        console.log('Refresh token is present = ', refreshToken);
        const tokenParts = JSON.parse(atob(refreshToken.split('.')[1]));

        // exp date in token is expressed in seconds, while now() returns milliseconds:
        const now = Math.ceil(Date.now() / 1000);
        console.log(tokenParts.exp);

        // Case 3.a Refresh token is present and it is not expired - use it to get new access token

        if (tokenParts.exp > now) {
          return axiosInstance
            .post('auth/api/token/refresh/', { refresh: refreshToken })
            .then((response) => {
              localStorage.setItem('access_token', response.data.access);

              axiosInstance.defaults.headers['Authorization'] =
                'Bearer ' + response.data.access;
              originalRequest.headers['Authorization'] =
                'Bearer ' + response.data.access;

              console.log('access token updated');

              // After refreshing the token request again user's previous url
              // which was blocked due to unauthorized error

              // I am not sure by default axios performs get request
              // But since we are passing the entire config of previous request
              // It seems to perform same request method as previous

              return axiosInstance(originalRequest);
            })

            .catch((err) => {
              // If any error occurs at this point we cannot guess what it is
              // So just console log it

              console.log(err);
            });
        } else {
          // Refresh token is expired ask user to login again.

          console.log('Refresh token is expired', tokenParts.exp, now);
          store.dispatch(setLoginFalse());
        }
      } else {
        // refresh token is not present in local storage so ask user to login again

        console.log('Refresh token not available.');
        store.dispatch(setLoginFalse());
      }
    }

    // specific error handling done elsewhere
    return Promise.reject(error);
  },
);
export default axiosInstance;

请注意,我在 axiosIntance 中将 Content-Type 设置为 'application/json'。

但我的问题是为了上传图片内容类型应该是'multipart/form-data --boundary: set-automatically'。

(注意:手动设置多部分数据的边界似乎不起作用)

如果我们不将 content-type 放在 header 中,则 axios 会自动设置多部分数据的边界。但为此,我必须以某种方式从 axiosInstance 的一个位置(从我上传图像的位置)删除内容类型,而不会干扰项目其他部分使用的 axiosInstance。

我使用 fetch 对其进行了测试,并通过设置新的 axios 实例,它按预期工作。但问题是,如果需要,这些请求不会被 axios 拦截以刷新 JWT 令牌。

我阅读了有关此的各种帖子,但我仍然没有找到解决此问题的方法。

如果需要,我无法提供更多详细信息。请帮帮我,我已经花了 8 个多小时来调试这个。

谢谢。

编辑 1

我把handleSubmit函数改成了这个


  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(file);

    let formData = new FormData();
    formData.append('profile_pic', file);
    formData.append('name', 'root');

    axiosInstance.defaults.headers.common['Content-Type'] =
      'multipart/form-data';

    axiosInstance
      .put('/users/profile-pic-upload/', formData)
      .then((res) => console.log(res))
      .catch((err) => console.log(err));
  };

但是内容类型还是application/json

但是假设我将核心 axios.js 中的内容类型更改为“multipart/form-data”,它会更改所有请求的内容类型。它会破坏其他东西,但正如预期的那样,它不会解决这个问题。因为设置手动边界似乎不起作用。甚至this 帖子都说要在多部分数据期间删除内容类型,以便由库自动处理(在这种情况下为 axios)

【问题讨论】:

    标签: reactjs axios


    【解决方案1】:

    要将任何动态传递给您的 axios 实例,请使用返回 axios 实例的函数,如下所示:

    import axios from 'axios';
    
    const customAxios = (contentType) => {
      // axios instance for making requests
      const axiosInstance = axios.create({
        // your other properties for axios instance
        headers: {
          'Content-Type': contentType,
        },
      });
    
      // your response interceptor
      axiosInstance.interceptors.response.use(// handle response);
    
      return axiosInstance;
    };
    
    export default customAxios;
    

    现在,您可以像这样使用 axios:

    import customAxios from './customAxios';
    
    const axiosForJSON = customAxios('application/json');
    const axiosForMultipart = customAxios('multipart/form-data');
    
    axiosForJSON.get('/hello');
    axiosForMultipart.post('/hello', {});
    
    // OR
    cusomAxios('application/json').get('/hello');
    

    【讨论】:

    • 非常感谢。这正是我一直在寻找的东西。我有一个问题,早些时候我在整个项目中使用了一个 axiosInstance。但是现在每次我导入 axiosinstance 都会创建并返回一个新实例,这会导致任何性能问题吗? ,因为每个实例都附带一个拦截器。
    • 您可以通过contentType 拨打电话memoize。您不必这样做,但假设您有 lodash's memoize 可用:const customAxios = _.memoize((contentType) => { ... });
    【解决方案2】:
    axiosInstance.defaults.headers.put['Content-Type'] = "multipart/form-data";
    

    或者

    axiosInstance.interceptors.request.use(config => {
      config.headers.put['Content-Type'] = 'multipart/form-data';
      return config;
    });
    

    针对您的具体实例试试这个。

    【讨论】:

    • 您好,感谢您的回复。我编辑了这个问题。请查看编辑。
    • @SankethB.K 请检查最新更新。
    猜你喜欢
    • 2011-01-05
    • 2021-11-25
    • 2011-01-10
    • 1970-01-01
    • 2015-09-24
    • 2014-08-20
    • 1970-01-01
    • 2018-12-31
    • 2020-06-16
    相关资源
    最近更新 更多