J'ai une application react/redux qui récupère un jeton d'un serveur api. Après l’authentification de l’utilisateur, je souhaite que toutes les demandes axios aient ce jeton comme en-tête d’autorisation sans devoir 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 hits de qualité sur Google.
Voici ma configuration de 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 suis un peu perdu sur la façon de procéder. J'ai essayé de créer une instance axios dans un fichier de mon répertoire racine et de mettre à jour/import à la place de node_modules, mais il n'est pas associé à l'en-tête lorsque l'état change. Tous les commentaires/idées sont très appréciés, merci.
Dans la documentation de axios
, vous pouvez voir qu’il existe un mécanisme permettant de définir l’en-tête par défaut qui sera envoyé à chaque demande.
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 elle-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, il n'est plus nécessaire d'attacher manuellement un jeton à chaque requête. Vous pouvez placer la fonction ci-dessus dans le fichier dont l'exécution est garantie à chaque fois (e.g: Fichier contenant les itinéraires).
J'espère que ça aide :)
La meilleure solution pour moi est de créer un service client que vous allez instancier avec votre jeton et l'utiliser 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 à partir du cookie localStorage /, comme vous le souhaitez.
Si vous utilisez la version "axios": "^ 0.17.1", procédez comme suit:
Créer une instance de 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.
De même, nous avons une fonction pour définir ou supprimer le jeton d'appels comme ceci:
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 lors de l'initialisation, puis établissons celui reçu.
Si vous souhaitez appeler d'autres itinéraires d'API ultérieurement et conserver votre jeton dans le magasin, essayez en utilisant le middleware redux .
Le middleware pourrait écouter l'action 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);
};
Il arrive parfois que certaines des demandes effectuées avec axios soient dirigées vers des systèmes d'extrémité n'acceptant 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 tout fichier exécuté chaque fois que l’application React s’exécute, par exemple dans le fichier itinéraires.
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);
});
}