在 axios 设置 auth headers 之前响应调用 API 请求

问题描述 投票:0回答:1

我正在尝试将 AuthContext 设置为 React,但遇到了一个问题:React 在 axios 设置请求中的 auth 标头之前调用 API。这会导致首次渲染时 API 失败

missing authentication parameters

我将提供

AuthContext
AuthService
App

的较小示例

AuthContext

import React, { createContext, useState, useEffect, useContext, useCallback } from "react";
import AuthService from "./authService";

// Hardcoded token for testing
const hardcodedToken = "your-hardcoded-token-here";

// Create the AuthContext
const AuthContext = createContext(null);

export const AuthContextProvider = ({ children }) => {
  const [token, setToken] = useState(null);
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  const checkIfLogged = useCallback(async () => {
    const token = hardcodedToken;

    if (token) {
      setToken(token);
      setIsAuthenticated(true);

      // Set Axios defaults and mark auth as ready
      authService.setAxiosDefaults(token);
    } else {
      setIsAuthenticated(false);
      authService.TODOLogout();
    }
  }, []);

  useEffect(() => {
    checkIfLogged();
  }, [checkIfLogged]);

  return (
    <AuthContext.Provider value={{ token, isAuthenticated }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

AuthService

import axios from "axios";

class AuthService {
  constructor(authRedirectUrlPrefix) {
    this.authRedirectUrlPrefix = authRedirectUrlPrefix;
  }


  /** Set default axios headers and interceptors */
  setAxiosDefaults(token) {
    debugger;
    // Pass auth token in each request
    axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
    axios.defaults.headers.common["Content-Type"] = "application/json";

    axios.interceptors.response.use(
      (response) => {
        return response; // Ignore successful responses
      },
      (error) => {
        // If response contains auth error, redirect to login
        if (error.response.status === 401) {
          this.redirectToLogout(false);
        }
        return error;
      }
    );
  }
}

export default AuthService;

App

import "./App.css";
import { Route, Switch, Redirect, BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";

import { createTheme, ThemeProvider } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
import Box from "@mui/material/Box";
import Toolbar from "@mui/material/Toolbar";
import Container from "@mui/material/Container";

import Header from "./layout/Header";
import store from "./store";
import { Home } from "./components/Home";

import { AuthContextProvider } from "./auth/AuthContext";
import { ApiContextProvider } from "./context/ApiContext";

const mdTheme = createTheme();

function App() {
  return (
    <AuthContextProvider>
        <ApiContextProvider>
          <BrowserRouter>
            <ThemeProvider theme={mdTheme}>
              <Box sx={{ display: "flex" }}>
                <CssBaseline />
                <Header />
                <Box
                  component="main"
                  sx={{
                    backgroundColor: (theme) =>
                      theme.palette.mode === "light"
                        ? theme.palette.grey[300]
                        : theme.palette.grey[900],
                    flexGrow: 1,
                    height: "100vh",
                    overflow: "auto",
                  }}
                >
                  <Toolbar />
                  <Container maxWidth="lg" sx={{ mt: 3, mb: 3 }}>
                    <Switch>
                      <Route exact path="/" component={Home} />
                      <Redirect from="*" to="/" />
                    </Switch>
                  </Container>
                </Box>
              </Box>
            </ThemeProvider>
          </BrowserRouter>
        </ApiContextProvider>
    </AuthContextProvider>
  );
}

export default App;

现在,考虑到所有这些设置,发生的情况是我的 React 应用程序在设置 auth 标头之前调用第一个 api 请求。我可以这么说,因为我在这里添加了一个

debugger
,并且在重新加载应用程序时首先会点击它。

import React, { createContext, useContext, useMemo } from "react";
import axios from "axios";
// import config from "../config";

const ApiClient = {
  getData: async () => {
    // debugger;
    const response = await axios.get(/demo/data);
    return response.data;
  },
};

// Create the API Client Context
export const ApiClientContext = createContext(ApiClient);

export const useApiClient = () => {
  return useContext(ApiClientContext);
};

// Provider component to wrap around your app
export const ApiContextProvider = ({ children }) => {
  const value = useMemo(() => {
    return ApiClient;
  }, []);

  return (
    <ApiContextProvider.Provider value={value}>
      {children}
    </ApiContextProvider.Provider>
  );
};
javascript reactjs react-hooks axios react-context
1个回答
0
投票

问题的原因似乎是在

token
初始化为
null
和更新为正确值之间的短时间内发出第一个请求。这是一个完全有效的场景。为了避免这种情况,我们可以在发出请求之前获取
const { isAuthenticated } = useAuth();
并等待
isAuthenticated
变为
true

以下是实现此目的的一种方法。

const ApiClient = (isAuthenticated) => ({
  getData: async () => {
    if (isAuthenticated) {
      const response = await axios.get(/demo/data);
      return response.data;
    } else {
      // don't call the endpoint when not authenticated
      return undefined;
    }
  }),
};

export const ApiContextProvider = ({ children }) => {
  const { isAuthenticated } = useAuth();

  const value = useMemo(() => {
    return ApiClient(isAuthenticated);
  }, [isAuthenticated]);

  return (
    <ApiContextProvider.Provider value={value}>
      {children}
    </ApiContextProvider.Provider>
  );
};
© www.soinside.com 2019 - 2024. All rights reserved.