Dołącz nagłówek Authorization dla wszystkich żądań Axios

131

Mam aplikację react / redux, która pobiera token z serwera API. Po uwierzytelnieniu użytkownika chciałbym, aby wszystkie żądania axios miały ten token jako nagłówek autoryzacji bez konieczności ręcznego dołączania go do każdego żądania w akcji. Jestem dość nowy, jeśli chodzi o reakcję / redukcję i nie jestem pewien najlepszego podejścia i nie znajduję żadnych wysokiej jakości hitów w Google.

Oto moja konfiguracja 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;
  }
}

Mój token jest przechowywany w sklepie Redux pod adresem state.session.token.

Jestem trochę zagubiony, jak postępować. Próbowałem utworzyć instancję axios w pliku w moim katalogu głównym i zaktualizować / zaimportować to zamiast z node_modules, ale nie dołącza nagłówka, gdy zmienia się stan. Wszelkie opinie / pomysły są bardzo mile widziane, dzięki.

awwester
źródło
Gdzie przechowujesz token autoryzacyjny po odebraniu tokena z serwera? Lokalny magazyn?
Hardik Modha
w sklepie Redux session.token
awwester

Odpowiedzi:

203

Można to osiągnąć na wiele sposobów. Tutaj wyjaśniłem dwa najczęściej stosowane podejścia.

1. Możesz użyć przechwytywaczy axios do przechwytywania żądań i dodawania nagłówków autoryzacji.

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

    return config;
});

2. Z dokumentacji od axioswidać nie jest dostępny mechanizm, który pozwala na ustawienie domyślnego nagłówka, które będą wysyłane z każdym zamówienie można zrobić.

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

Więc w twoim przypadku:

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

Jeśli chcesz, możesz utworzyć samowykonalną funkcję, która sama ustawi nagłówek autoryzacji, gdy token jest obecny w sklepie.

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

Teraz nie musisz już ręcznie dołączać tokena do każdego żądania. Możesz umieścić powyższą funkcję w pliku, który będzie wykonywany za każdym razem ( np.: Plik zawierający trasy).

Mam nadzieję, że to pomoże :)

Hardik Modha
źródło
1
już używam redux-persist, ale przyjrzę się oprogramowaniu pośredniczącemu, aby dołączyć token w nagłówku, dzięki!
awwester
1
@awwester Nie potrzebujesz oprogramowania pośredniczącego, aby dołączyć token w nagłówku. Dołączenie tokena do nagłówka jest axios.defaults.header.common[Auth_Token] = tokentakie proste.
Hardik Modha
4
@HardikModha Jestem ciekaw, jak można to zrobić za pomocą Fetch API.
Rowland
@Rowland Wierzę, że aby osiągnąć to samo, musisz napisać opakowanie nad pobieranym API. Szczegółowa odpowiedź na to pytanie nie wchodzi w zakres pytania zadanego przez PO. Możesz zadać inne pytanie :)
Hardik Modha
2
Cześć @HardikModha. Jeśli używam domyślnych nagłówków dla ustawionego tokenu, gdy chcę odnowić token, nie można go ponownie ustawić w nagłówku. czy to jest poprawne? Więc muszę użyć przechwytywaczy.
beginerdeveloper
50

Jeśli używasz wersji „axios”: „^ 0.17.1”, możesz to zrobić w ten sposób:

Utwórz instancję 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;
  });

Następnie dla każdego żądania token zostanie wybrany z localStorage i zostanie dodany do nagłówków żądania.

Używam tego samego wystąpienia w całej aplikacji z tym kodem:

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

Powodzenia.

llioor
źródło
@ NguyễnPhúc Z przyjemnością chodzi o użycie „przechwytywaczy” axios
llioor
To najlepsza odpowiedź ... aby zainicjować token na przechwytywaczach dla każdego żądania! . Dzięki
James Ace
45

Najlepszym rozwiązaniem dla mnie jest utworzenie usługi klienckiej, której instancję utworzysz za pomocą tokenu i użyjesz jej do zawijania 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);

W tym kliencie możesz również pobrać token z localStorage / cookie, jak chcesz.

Kmaschta
źródło
1
A co jeśli chcesz, aby request.get () miał nagłówki „application-type”. Przy twoim podejściu nagłówki z defaultOptions zostaną nadpisane przez nagłówki z żądania. Mam rację? Dziękuję Ci.
nipuro
9

Podobnie mamy funkcję ustawiania lub usuwania tokena z wywołań takich jak ta:

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

Zawsze czyścimy istniejący token podczas inicjalizacji, a następnie ustalamy otrzymany.

elQueFaltaba
źródło
5

Jeśli chcesz w przyszłości wywoływać inne trasy API i zachować swój token w sklepie, spróbuj użyć oprogramowania pośredniego Redux .

Oprogramowanie pośredniczące może nasłuchiwać akcji api i odpowiednio wysyłać żądania API za pośrednictwem axios.

Oto bardzo podstawowy przykład:

działania / 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'
  };
}

oprogramowanie pośredniczące / 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);
};
Paweł. b
źródło
3

Czasami pojawia się przypadek, w którym niektóre żądania wysłane za pomocą axios są wskazywane na punkty końcowe, które nie akceptują nagłówków autoryzacji. Zatem alternatywnym sposobem ustawienia nagłówka autoryzacji tylko w dozwolonej domenie jest taki, jak w poniższym przykładzie. Umieść następującą funkcję w dowolnym pliku, który jest wykonywany za każdym razem, gdy aplikacja React jest uruchamiana, na przykład w pliku tras.

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

}
Karolis Ramanauskas
źródło
0

Spróbuj utworzyć nową instancję, tak jak to zrobiłem poniżej

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

Jak tego użyć

common_axios.get(url).......
common_axios.post(url).......
ugali miękkie
źródło
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);

Wpadłem na kilka pułapek, próbując wdrożyć coś podobnego i na podstawie tych odpowiedzi wymyśliłem to. Problemy, których doświadczałem, to:

  1. Jeśli używasz axios do żądania pobrania tokena w twoim sklepie, musisz wykryć ścieżkę przed dodaniem nagłówka. Jeśli tego nie zrobisz, spróbuje dodać nagłówek również do tego wywołania i dostać się do problemu z okrężną ścieżką. Działałaby również odwrotność dodawania wyrażenia regularnego w celu wykrycia innych wywołań
  2. Jeśli sklep zwraca obietnicę, musisz zwrócić wywołanie do sklepu, aby rozwiązać obietnicę w funkcji authHandler. Funkcjonalność Async / Await uczyniłaby to łatwiejszym / bardziej oczywistym
  3. Jeśli wywołanie tokena uwierzytelniania nie powiedzie się lub jest to wezwanie do uzyskania tokena, nadal chcesz rozwiązać obietnicę za pomocą konfiguracji
Bhetzie
źródło
0

Chodzi o to, aby ustawić token na przechwytywaczach dla każdego żądania

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;
});
James Ace
źródło