reactjs AxiosReact中动态变化的内容类型

aor9mmx1  于 2023-03-12  发布在  React
关注(0)|答案(4)|浏览(79)

这就是我的问题。我在我的项目中使用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中将内容类型设置为“application/json”。
但我的问题是,为了上传图像,内容类型应该是“multipart/form-data --boundary:自动设置“。
(NOTE:手动设置多部分数据的边界似乎不起作用)
如果我们不把content-type放在header中,那么多部分数据的边界是由axios自动设置的,但为此我必须在一个地方(我上传图像的地方)从axiosInstance中删除content-type,而不干扰项目其他部分使用的axiosInstance。
我用fetch测试了它,通过设置新的axios示例,它可以按预期工作,但问题是如果需要刷新JWT令牌,这些请求不会被axios拦截。
我读了很多关于这个问题的帖子,但我仍然没有找到解决这个问题的方法。
我不能提供任何更多的细节,如果需要的话。请帮助我,我已经花了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 x1c 0d1x
但是让我们假设我在核心axios.js中将内容类型更改为“multipart/form-data”,它会更改所有请求的内容类型。它会破坏其他东西,但正如预期的那样,它不会修复这个问题。因为设置手动边界似乎不起作用。甚至this帖子也说在多部分数据期间删除内容类型,以便由库(在本例中为axios)自动处理。

svmlkihl

svmlkihl1#

要向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;

现在,您可以使用如下axio:

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');
ego6inou

ego6inou2#

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

或者

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

请为您的特定示例尝试此操作。

chhqkbe1

chhqkbe13#

上面来自Lovlesh Pokra的Answer帮助了我。
在我下载文件时检查访问令牌的情况下,需要解析响应以获取新的访问令牌。但是,由于此拦截器位于用于下载文件的类中,并且在创建时将responseType设置为arrayBuffer

responseType: 'arraybuffer'

我不得不将responseType更改为json,如下所示

youraxiosinstance.defaults.responseType = "json";

然后将其设置回arraybuffer -以便文件下载可以继续

youraxiosinstance.defaults.responseType = "arraybuffer";

根据您的需要--就在呼叫之前--可以按照您的要求进行更改。

mitkmikd

mitkmikd4#

我也在为上传媒体到服务器而挣扎。因为我的全局Axios示例的内容类型为“application/json”,所以当发出请求时,我使用下面的脚本将内容类型更新为“multipart/form-data”。

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

由于“网络”选项卡中的请求标头仍包含全局配置中的“application/json”,因此仍未更新(这可能是原因-全局标头保存在其他引用中,我们正在其他引用中更新它)

因此,修复方法是在请求传输之前拦截请求,然后按如下所示修改标头

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

一旦设置了内容类型“multipart/form-data”,Axios将自动处理边界

希望这对你或别人有帮助。谢谢!
快乐编码:-)

相关问题