Đính kèm tiêu đề Ủy quyền cho tất cả các yêu cầu axios


128

Tôi có một ứng dụng react / redux tìm nạp mã thông báo từ máy chủ api. Sau khi người dùng xác thực, tôi muốn làm cho tất cả các yêu cầu axios có mã thông báo đó làm tiêu đề Ủy quyền mà không cần phải đính kèm thủ công nó vào mọi yêu cầu trong hành động. Tôi còn khá mới đối với react / redux và không chắc về cách tiếp cận tốt nhất và không tìm thấy bất kỳ lượt truy cập chất lượng nào trên google.

Đây là thiết lập redux của tôi:

// 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ã thông báo của tôi được lưu trữ trong cửa hàng redux dưới state.session.token.

Tôi hơi mất cách để tiếp tục. Tôi đã thử tạo một phiên bản axios trong một tệp trong thư mục gốc của mình và cập nhật / nhập tệp đó thay vì từ node_modules nhưng nó không đính kèm tiêu đề khi trạng thái thay đổi. Bất kỳ phản hồi / ý tưởng được đánh giá cao, cảm ơn.


Bạn đang lưu trữ mã thông báo ủy quyền ở đâu sau khi nhận được mã thông báo từ máy chủ? lưu trữ cục bộ?
Hardik Modha

trong cửa hàng redux session.token
awwester,

Câu trả lời:


199

Có nhiều cách để đạt được điều này. Ở đây, tôi đã giải thích hai cách tiếp cận phổ biến nhất.

1. Bạn có thể sử dụng bộ chặn axios để chặn bất kỳ yêu cầu nào và thêm tiêu đề ủy quyền.

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

    return config;
});

2. Từ tài liệu của axiosbạn, bạn có thể thấy có một cơ chế có sẵn cho phép bạn đặt tiêu đề mặc định sẽ được gửi với mọi yêu cầu bạn đưa ra.

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

Vì vậy, trong trường hợp của bạn:

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

Nếu muốn, bạn có thể tạo một hàm tự thực thi sẽ tự đặt tiêu đề ủy quyền khi mã thông báo có mặt trong cửa hàng.

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

Giờ đây, bạn không cần phải đính kèm mã thông báo theo cách thủ công cho mọi yêu cầu nữa. Bạn có thể đặt hàm trên trong tệp được đảm bảo sẽ được thực thi mọi lúc ( ví dụ: Tệp chứa các tuyến).

Hy vọng nó giúp :)


1
đã sử dụng redux-Kiên trì nhưng sẽ xem xét phần mềm trung gian để đính kèm mã thông báo trong tiêu đề, cảm ơn!
awwester,

1
@awwester Bạn không cần phần mềm trung gian để đính kèm mã thông báo trong tiêu đề. Đính kèm mã thông báo trong tiêu đề axios.defaults.header.common[Auth_Token] = tokenđơn giản như vậy.
Hardik Modha

4
@HardikModha Tôi tò mò làm cách nào một người có thể thực hiện điều này với API Tìm nạp.
Rowland

@Rowland Tôi tin rằng, bạn sẽ cần viết một trình bao bọc trên API tìm nạp để đạt được điều tương tự. Câu trả lời chi tiết cho câu hỏi đó nằm ngoài phạm vi câu hỏi của OP. Bạn có thể đặt câu hỏi khác :)
Hardik Modha

2
Xin chào @HardikModha. Nếu tôi sử dụng tiêu đề mặc định cho mã thông báo đã đặt khi tôi muốn gia hạn mã thông báo, nó không thể đặt lại thành tiêu đề. nó có đúng không? Vì vậy, tôi phải sử dụng các chốt chặn.
askinerdeveloper

50

Nếu bạn sử dụng phiên bản "axios": "^ 0.17.1", bạn có thể làm như sau:

Tạo phiên bản 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;
  });

Sau đó, đối với bất kỳ yêu cầu nào, mã thông báo sẽ được chọn từ localStorage và sẽ được thêm vào các tiêu đề yêu cầu.

Tôi đang sử dụng cùng một phiên bản trên tất cả ứng dụng với mã này:

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

Chúc may mắn.


@ NguyễnPhúc Rất vui, ý chính là dùng "interceptors" của axios
llioor 30/1118

Đây là câu trả lời tốt nhất ... để khởi tạo mã thông báo trên bộ chặn cho mỗi yêu cầu! . Cảm ơn
james ace

45

Giải pháp tốt nhất đối với tôi là tạo một dịch vụ khách hàng mà bạn sẽ khởi tạo với mã thông báo của mình và sử dụng nó để kết thúc 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);

Trong ứng dụng khách này, bạn cũng có thể lấy mã thông báo từ localStorage / cookie, như bạn muốn.


1
Điều gì xảy ra nếu bạn muốn tạo request.get () với tiêu đề "application-type". Với cách tiếp cận của bạn, các tiêu đề từ defaultOptions sẽ bị ghi đè bởi các tiêu đề từ yêu cầu. Tôi đã đúng? Cảm ơn bạn.
nipuro

9

Tương tự, chúng tôi có một chức năng để đặt hoặc xóa mã thông báo khỏi các cuộc gọi như thế này:

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

Chúng tôi luôn làm sạch mã thông báo hiện có khi khởi tạo, sau đó thiết lập mã đã nhận.


5

Nếu bạn muốn gọi các tuyến api khác trong tương lai và giữ mã thông báo của mình trong cửa hàng thì hãy thử sử dụng phần mềm trung gian redux .

Phần mềm trung gian có thể lắng nghe hành động của api và gửi các yêu cầu api thông qua axios cho phù hợp.

Đây là một ví dụ rất cơ bản:

hành động / 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

Đôi khi bạn gặp trường hợp một số yêu cầu được thực hiện với axios được trỏ đến các điểm cuối không chấp nhận tiêu đề ủy quyền. Do đó, cách thay thế để đặt tiêu đề ủy quyền chỉ trên miền được phép là như trong ví dụ bên dưới. Đặt hàm sau vào bất kỳ tệp nào được thực thi mỗi khi ứng dụng React chạy, chẳng hạn như trong tệp định tuyến.

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

Cố gắng tạo phiên bản mới như tôi đã làm dưới đây

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

Làm thế nào để sử dụng nó

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

Gặp phải một số vấn đề khi cố gắng thực hiện một cái gì đó tương tự và dựa trên những câu trả lời này, đây là những gì tôi đã nghĩ ra. Các vấn đề tôi gặp phải là:

  1. Nếu sử dụng axios cho yêu cầu nhận mã thông báo trong cửa hàng của bạn, bạn cần phát hiện đường dẫn trước khi thêm tiêu đề. Nếu bạn không, nó cũng sẽ cố gắng thêm tiêu đề vào cuộc gọi đó và gây ra sự cố đường dẫn tròn. Ngược lại của việc thêm regex để phát hiện các cuộc gọi khác cũng sẽ hoạt động
  2. Nếu cửa hàng đang trả lại một lời hứa, bạn cần trả lại lệnh gọi đến cửa hàng để giải quyết lời hứa trong hàm authHandler. Chức năng Async / Await sẽ giúp việc này trở nên dễ dàng / rõ ràng hơn
  3. Nếu lệnh gọi mã thông báo xác thực không thành công hoặc lệnh gọi để lấy mã thông báo, bạn vẫn muốn giải quyết một lời hứa với cấu hình

0

Vấn đề là đặt mã thông báo trên các bộ chặn cho mỗi yêu cầu

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;
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.