You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RTK Query无法携带授权Token获取用户配置文件问题求助

RTK Query无法在请求头中携带Token获取用户配置文件

问题描述

在React + Redux Toolkit(RTK)课程项目中,已通过RTK Query的POST请求完成登录并成功获取Token,但调用获取用户配置文件的POST接口(/user/profile)时,无法将Token放入请求头。核心问题是getState().auth.data.body.token无法正确获取Token,不清楚状态的正确路径。

API信息

  • POST /user/login:请求正常,返回包含Token的响应
  • POST /user/profile:需在请求头携带Token才能获取响应,目前无法成功请求

相关代码

store.js

import { configureStore } from '@reduxjs/toolkit'
import { bankApi } from './ApiSlice.js'

export const store = configureStore({
  reducer: {
    [bankApi.reducerPath]: bankApi.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(bankApi.middleware),
})

ApiSlice.js

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

const apiBaseUrl = 'http://localhost:3001/api/v1';

export const bankApi = createApi({
  reducerPath: 'bankApi',
  baseQuery: fetchBaseQuery({
    baseUrl: apiBaseUrl,
    prepareHeaders: (headers, { getState }) => {
      console.log('prepareHeaders is called');
      const token = getState().auth.data.body.token;
      if (token) {
        headers.set('Authorization', `Bearer ${token}`);
      }
      return headers;
    },
  }),
  endpoints: (builder) => ({
    auth: builder.mutation({
      query: (credentials) => ({
        url: '/user/login',
        method: 'POST',
        body: credentials,
      }),
    }),
    getProfile: builder.mutation({
      query: () => ({
        url: '/user/profile',
        method: 'POST',
      }),
    }),
    updateProfile: builder.query({
      query: () => ({
        url: '/user/profile',
        method: 'PUT',
      }),
    }),
  }),
})

export const {
  useAuthMutation,
  useGetProfileMutation,
  useUpdateProfileQuery
} = bankApi

Loginpage.jsx

import { useState } from 'react';
import { useAuthMutation, useGetProfileMutation } from '../rtk/ApiSlice'

export default function LoginPage(){
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const [
    login,
    {
      isLoading: loginIsLoading,
      isError: loginIsError,
      isSuccess: loginIsSuccess,
      data: loginData,
      error: loginError
    }
  ] = useAuthMutation();

  const [
    profile,
    {
      isError: profileIsError,
      error: profileError,
      data: profileData
    }
  ] = useGetProfileMutation();
    
  const handleLogin = (e) => {
    e.preventDefault();
    login({ email, password });
  };

  const handleProfile = () => {
    profile();
  };
    
  return (
    <div className="main bg-dark">
      <section className="sign-in-content">
        <i className="fa fa-user-circle sign-in-icon"></i>
        <h1>Sign In</h1>
        <form
          onSubmit={(e) => {
            e.preventDefault();
            handleLogin();
          }}
        >
          <div className="input-wrapper">
            <label>Username</label>
            <input
              type="text"
              id="email"
              placeholder=""
              value={email}
              onChange={(e) => setEmail(e.target.value)}
            />
          </div>
          <div className="input-wrapper">
            <label>Password</label>
            <input
              type="password"
              id="password"
              placeholder=""
              value={password}
              onChange={(e) => setPassword(e.target.value)}
            />
          </div>
          <div className="input-remember">
            <input type="checkbox" id="remember-me" />
            <label>Remember me</label>
          </div>
          <button
            className="sign-in-button"
            type="submit"
            disabled={loginIsLoading}
          >
            {loginIsLoading ? 'Signing in...' : 'Sign in'}
          </button>
        </form>
        {loginIsError && <p className='perror'>
          {loginError.data.status} {loginError.data.message}
        </p>}
        {loginIsSuccess && <>
          <p className='psuccess'>
            {loginData.message} Token: {loginData.body.token}
          </p>
          <button onClick={handleProfile}>
            Get Profile
          </button>
        </>}

        {profileIsError && <p className='perror'>
          {profileError.data.status} {profileError.data.message}
        </p>}
        {profileData && <p className='psuccess'>
          {profileData.message} First Name: {profileData.body.firstName} Last Name: {profileData.body.lastName} Surname: {profileData.body.userName}
        </p>}   
      </section>
    </div>
  );
}

已尝试的解决方法

  • 查阅RTK Query官方文档
  • 观看RTK Query相关教程
  • 通过prepareHeaders尝试获取并设置Token
  • 在getProfile接口中单独配置prepareHeaders
  • 在Loginpage.jsx中直接传递Token给useGetProfileMutation
  • 将Token存入localStorage后取出使用
  • 等待登录成功后再调用获取配置文件接口

解决方案

问题根源

你的Redux Store中仅注册了RTK Query的bankApi reducer,不存在名为auth的切片,因此getState().auth路径无效。RTK Query的mutation响应数据默认存储在bankApi的缓存中,但该缓存路径不稳定,不建议直接读取。

方法一:创建独立Auth切片存储Token

1. 新建authSlice.js

import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  token: null,
};

const authSlice = createSlice({
  name: 'auth',
  initialState,
  reducers: {
    setToken: (state, action) => {
      state.token = action.payload;
    },
    clearToken: (state) => {
      state.token = null;
    },
  },
});

export const { setToken, clearToken } = authSlice.actions;
export default authSlice.reducer;

2. 更新store.js,添加Auth reducer

import { configureStore } from '@reduxjs/toolkit'
import { bankApi } from './ApiSlice.js'
import authReducer from './authSlice.js'

export const store = configureStore({
  reducer: {
    auth: authReducer,
    [bankApi.reducerPath]: bankApi.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(bankApi.middleware),
})

3. 修改Loginpage.jsx,登录成功后保存Token

import { useState } from 'react';
import { useDispatch } from 'react-redux';
import { useAuthMutation, useGetProfileMutation } from '../rtk/ApiSlice';
import { setToken } from '../rtk/authSlice';

export default function LoginPage(){
  const dispatch = useDispatch();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const [
    login,
    {
      isLoading: loginIsLoading,
      isError: loginIsError,
      isSuccess: loginIsSuccess,
      data: loginData,
      error: loginError
    }
  ] = useAuthMutation();

  const [
    profile,
    {
      isError: profileIsError,
      error: profileError,
      data: profileData
    }
  ] = useGetProfileMutation();
    
  const handleLogin = (e) => {
    e.preventDefault();
    login({ email, password }).unwrap()
      .then((data) => {
        dispatch(setToken(data.body.token));
      })
      .catch((err) => {
        // 处理登录错误
      });
  };

  // 其余代码保持不变
}

4. 更新ApiSlice.js的prepareHeaders路径

prepareHeaders: (headers, { getState }) => {
  console.log('prepareHeaders is called');
  const token = getState().auth.token;
  if (token) {
    headers.set('Authorization', `Bearer ${token}`);
  }
  return headers;
},

方法二:使用localStorage存储Token(更简单)

1. 修改Loginpage.jsx的登录逻辑

const handleLogin = (e) => {
  e.preventDefault();
  login({ email, password }).unwrap()
    .then((data) => {
      localStorage.setItem('authToken', data.body.token);
    })
    .catch((err) => {
      // 处理登录错误
    });
};

2. 更新ApiSlice.js的prepareHeaders

prepareHeaders: (headers) => {
  const token = localStorage.getItem('authToken');
  if (token) {
    headers.set('Authorization', `Bearer ${token}`);
  }
  return headers;
},

补充说明

  • 方法一使用Redux状态管理Token,适合需要全局共享登录状态的场景;方法二使用localStorage,实现更简单,且页面刷新后Token不会丢失。
  • 注意:使用localStorage存储Token时,需考虑XSS攻击风险,可结合其他安全措施(如HttpOnly Cookie),但课程项目中可暂时使用该方案。

内容的提问来源于stack exchange,提问作者Bidou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 05:08:16