Joindre l'en-tête d'autorisation pour toutes les demandes axios


130

J'ai une application react / redux qui récupère un jeton sur un serveur api. Une fois que l'utilisateur s'est authentifié, j'aimerais que toutes les demandes axios aient ce jeton comme en-tête d'autorisation sans avoir à le joindre manuellement à chaque demande de l'action. Je suis assez nouveau pour réagir / redux et je ne suis pas sûr de la meilleure approche et je ne trouve pas de résultats de qualité sur Google.

Voici ma configuration 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;
  }
}

Mon jeton est stocké dans le magasin redux sous state.session.token.

Je ne sais pas trop comment procéder. J'ai essayé de créer une instance axios dans un fichier de mon répertoire racine et de mettre à jour / importer cela au lieu de node_modules, mais cela n'attache pas l'en-tête lorsque l'état change. Tous les commentaires / idées sont très appréciés, merci.


Où stockez-vous le jeton d'autorisation une fois le jeton reçu du serveur? stockage local?
Hardik Modha

dans le magasin redux session.token
awwester

Réponses:


203

Il existe plusieurs façons d'y parvenir. Ici, j'ai expliqué les deux approches les plus courantes.

1. Vous pouvez utiliser les intercepteurs axios pour intercepter toutes les demandes et ajouter des en-têtes d'autorisation.

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

    return config;
});

2. Dans la documentation de axiosvous pouvez voir qu'il existe un mécanisme disponible qui vous permet de définir un en-tête par défaut qui sera envoyé avec chaque demande que vous faites.

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

Donc dans votre cas:

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

Si vous le souhaitez, vous pouvez créer une fonction auto-exécutable qui définira lui-même l'en-tête d'autorisation lorsque le jeton est présent dans le magasin.

(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'];
         */
     }
})();

Désormais, vous n'avez plus besoin d'attacher manuellement un jeton à chaque demande. Vous pouvez placer la fonction ci-dessus dans le fichier dont l'exécution est garantie à chaque fois ( ex: Fichier qui contient les routes).

J'espère que ça aide :)


1
utilise déjà redux-persist mais jettera un œil au middleware pour attacher le jeton dans l'en-tête, merci!
awwester

1
@awwester Vous n'avez pas besoin d'intergiciel pour attacher le jeton dans l'en-tête. Joindre un jeton dans l'en-tête est axios.defaults.header.common[Auth_Token] = tokenaussi simple que cela.
Hardik Modha

4
@HardikModha Je suis curieux de savoir comment on pourrait faire cela avec Fetch API.
Rowland

@Rowland Je crois que vous devrez écrire un wrapper sur l'API de récupération pour obtenir la même chose. La réponse détaillée à cette question sort du cadre de la question posée par le PO. Vous pouvez poser une autre question :)
Hardik Modha

2
Salut @HardikModha. Si j'utilise les en-têtes par défaut pour le jeton défini lorsque je souhaite renouveler le jeton, il ne peut pas être redéfini dans l'en-tête. est-ce correct? Je dois donc utiliser les intercepteurs.
beginerdeveloper

50

Si vous utilisez la version "axios": "^ 0.17.1", vous pouvez faire comme ceci:

Créer une instance d'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;
  });

Ensuite, pour toute demande, le jeton sera sélectionné dans localStorage et sera ajouté aux en-têtes de la demande.

J'utilise la même instance partout dans l'application avec ce code:

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

Bonne chance.


@ NguyễnPhúc Avec plaisir, tout l'intérêt est d'utiliser des "intercepteurs" d'axios
llio ou

C'est la meilleure réponse ... pour initialiser le token sur les intercepteurs à chaque requête! . Merci
james ace

45

La meilleure solution pour moi est de créer un service client que vous instanciez avec votre jeton et que vous utiliserez pour envelopper 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);

Dans ce client, vous pouvez également récupérer le jeton du localStorage / cookie, comme vous le souhaitez.


1
Que faire si vous souhaitez créer le request.get () avec des en-têtes de type "application". Avec votre approche, les en-têtes de defaultOptions seront écrasés par les en-têtes de request. J'ai raison? Je vous remercie.
nipuro

9

De même, nous avons une fonction pour définir ou supprimer le jeton des appels comme celui-ci:

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}`;
  }
}

Nous nettoyons toujours le jeton existant à l'initialisation, puis établissons celui reçu.


5

Si vous souhaitez appeler d'autres routes api à l'avenir et conserver votre jeton dans le magasin, essayez d' utiliser le middleware redux .

L'intergiciel peut écouter l'action an api et envoyer les requêtes API via axios en conséquence.

Voici un exemple très basique:

actions / api.js

export const CALL_API = 'CALL_API';

function onSuccess(payload) {
  return {
    type: 'SUCCESS',
    payload
  };
}

function onError(payload) {
  return {
    type: 'ERROR',
    payload,
    error: true
  };
}

export function apiLogin(credentials) {
  return {
    onSuccess,
    onError,
    type: CALL_API,
    params: { ...credentials },
    method: 'post',
    url: 'login'
  };
}

middleware / api.js

import axios from 'axios';
import { CALL_API } from '../actions/api';

export default ({ getState, dispatch }) => next => async action => {
  // Ignore anything that's not calling the api
  if (action.type !== CALL_API) {
    return next(action);
  }

  // Grab the token from state
  const { token } = getState().session;

  // Format the request and attach the token.
  const { method, onSuccess, onError, params, url } = action;

  const defaultOptions = {
    headers: {
      Authorization: token ? `Token ${token}` : '',
    }
  };

  const options = {
    ...defaultOptions,
    ...params
  };

  try {
    const response = await axios[method](url, options);
    dispatch(onSuccess(response.data));
  } catch (error) {
    dispatch(onError(error.data));
  }

  return next(action);
};

3

Parfois, vous obtenez un cas où certaines des requêtes effectuées avec axios sont pointées vers des points de terminaison qui n'acceptent pas les en-têtes d'autorisation. Ainsi, une autre façon de définir l'en-tête d'autorisation uniquement sur le domaine autorisé est comme dans l'exemple ci-dessous. Placez la fonction suivante dans n'importe quel fichier qui est exécuté à chaque exécution de l'application React, par exemple dans le fichier routes.

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

}

0

Essayez de créer une nouvelle instance comme je l'ai fait ci-dessous

var common_axios = axios.create({
    baseURL: 'https://sample.com'
});

// Set default headers to common_axios ( as Instance )
common_axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// Check your Header
console.log(common_axios.defaults.headers);

Comment l'utiliser

common_axios.get(url).......
common_axios.post(url).......

0
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);

Je suis tombé sur des pièges en essayant de mettre en œuvre quelque chose de similaire et sur la base de ces réponses, c'est ce que j'ai trouvé. Les problèmes que je rencontrais étaient:

  1. Si vous utilisez axios pour la demande d'obtenir un jeton dans votre magasin, vous devez détecter le chemin avant d'ajouter l'en-tête. Si vous ne le faites pas, il essaiera également d'ajouter l'en-tête à cet appel et de se retrouver dans un problème de chemin circulaire. L'inverse de l'ajout de regex pour détecter les autres appels fonctionnerait également
  2. Si le magasin renvoie une promesse, vous devez renvoyer l'appel au magasin pour résoudre la promesse dans la fonction authHandler. La fonctionnalité Async / Await rendrait cela plus facile / plus évident
  3. Si l'appel du jeton d'authentification échoue ou est l'appel pour obtenir le jeton, vous souhaitez toujours résoudre une promesse avec la configuration

0

Le but est de définir le jeton sur les intercepteurs pour chaque requête

import axios from "axios";
    
const httpClient = axios.create({
    baseURL: "http://youradress",
    // baseURL: process.env.APP_API_BASE_URL,
});

httpClient.interceptors.request.use(function (config) {
    const token = localStorage.getItem('token');
    config.headers.Authorization =  token ? `Bearer ${token}` : '';
    return config;
});
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.