登录功能异常排查:userInfo显示undefined及请求媒体类型错误
登录功能开发问题排查
问题描述
userInfo始终为undefined:使用Postman发送POST请求可正常获取用户信息及token,但Redux DevTools中无对应状态数据显示- 登录请求报错:
Unsupported media type "application/x-www-form-urlencoded, application/json" in request
相关代码
userReducers.js
export const userLoginReducers = (state = {}, action) => { switch (action.type) { case USER_LOGIN_REQUEST: return { laoding: true }; case USER_LOGIN_SUCCESS: return { laoding: false, userInfo: action.payload }; case USER_LOGIN_FAIL: return { laoding: false, error: action.payload }; case USER_LOGOUT: return {}; default: return state; } };
userActions.js
export const login = (email, password) => async (dispatch) => { try { dispatch({ type: USER_LOGIN_REQUEST, }); const config = { headers: { "Content-type": "application/json", }, }; const { data } = await axios.post( "/api/users/login/", { username: email, password: password }, config ); dispatch({ type: USER_LOGIN_SUCCESS, payload: data, }); localStorage.setItem("userInfo", JSON.stringify(data)); } catch (error) { dispatch({ type: USER_LOGIN_FAIL, payload: error.response && error.response.data.detail ? error.response.data.detail : error.message, }); } };
Store.js
import { legacy_createStore as createStore, combineReducers, applyMiddleware, } from "redux"; //import { configureStore } from "@reduxjs/toolkit"; import thunk from "redux-thunk"; import { composeWithDevTools } from "redux-devtools-extension"; import { productListReducers, productDetailsReducers, } from "./reducers/ProductReducers"; import { cartReducer } from "./reducers/CartReducers"; import { userLoginReducers } from "./reducers/UserReducers"; const reducer = combineReducers({ productList: productListReducers, productDetails: productDetailsReducers, cart: cartReducer, userLogin: userLoginReducers, }); const cartItemsFromStorage = localStorage.getItem("cartItems") ? JSON.parse(localStorage.getItem("cartItems")) : []; const userInfoFromStorage = localStorage.getItem("userInfo") ? JSON.parse(localStorage.getItem("userInfo")) : null; const initialState = { cart: { cartItems: cartItemsFromStorage }, userLogin: { userInfo: userInfoFromStorage }, }; const middleware = [thunk]; const store = createStore( reducer, initialState, composeWithDevTools(applyMiddleware(...middleware)) ); export default store;
LoginScreen.js
import React, { useState, useEffect } from "react"; import { Link } from "react-router-dom"; import { useDispatch, useSelector } from "react-redux"; import { Row, Col, Button, Form } from "react-bootstrap"; //import products from "../../products"; import Message from "../Message"; import Loader from "../Loader"; import { useNavigate, useLocation, useSearchParams } from "react-router-dom"; import { login } from "../../actions/UserActions"; import LoginForm from "../LoginForm"; function LoginScreen() { const [searchParams, setSearchParams] = useSearchParams(); const { search } = useLocation(); const navigate = useNavigate(); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const dispatch = useDispatch(); const redirect = searchParams.get(search.split("=")) || 1; const userLogin = useSelector((state) => state.userLogin); const { error, loading, userInfo } = userLogin; useEffect(() => { if (userInfo) { navigate(redirect); setSearchParams(searchParams); } }, [navigate, userInfo, redirect, searchParams, setSearchParams]); const submitHandler = (e) => { e.preventDefault(); dispatch(login(email, password)); }; return ( <LoginForm> <h1>Sign In</h1> {error && <Message variant="danger">{error}</Message>} {loading && <Loader />} <Form onSubmit={submitHandler}> <Form.Group> <Form.Label>Email Address</Form.Label> <Form.Control type="email" placeholder="Enter Email" value={email} onChange={(e) => setEmail(e.target.value)} ></Form.Control> </Form.Group> <Form.Group controlId="password"> <Form.Label>Password</Form.Label> <Form.Control type="password" placeholder="Enter Password" value={password} onChange={(e) => setPassword(e.target.value)} ></Form.Control> </Form.Group> <Button type="submit" className="mt-3" variant="primary"> Sign In </Button> </Form> <Row className="py-3"> <Col> New Customer? <Link to={redirect ? `/register?redirect=${redirect}` : "/register"}> Register </Link> </Col> </Row> </LoginForm> ); } export default LoginScreen;
问题分析与解决方案
问题2:Unsupported media type错误(优先解决)
这个错误是请求格式不兼容导致的,后端无法识别同时包含两种类型的请求头:
- 确认后端支持的Content-Type:
- 如果后端仅接受
application/x-www-form-urlencoded,修改userActions.js中的请求配置:import qs from 'qs'; // 需要先安装qs库:npm install qs // ... const { data } = await axios.post( "/api/users/login/", qs.stringify({ username: email, password: password }), // 转换为表单格式 { headers: { "Content-type": "application/x-www-form-urlencoded" } } ); - 如果后端接受
application/json,检查是否有axios拦截器或其他代码篡改了请求头,导致同时添加了两种Content-Type
- 如果后端仅接受
问题1:userInfo undefined与Redux DevTools无数据
这个问题和请求失败直接相关(请求失败不会触发USER_LOGIN_SUCCESS),同时存在代码拼写错误:
- 修正reducer中的拼写错误:所有
laoding都拼错成了loading,这会导致组件中useSelector获取loading状态失败,同时Redux状态结构异常:// 修正后的userReducers.js关键部分 case USER_LOGIN_REQUEST: return { loading: true }; case USER_LOGIN_SUCCESS: return { loading: false, userInfo: action.payload }; case USER_LOGIN_FAIL: return { loading: false, error: action.payload }; - 验证action常量一致性:确保
USER_LOGIN_REQUEST、USER_LOGIN_SUCCESS等常量在reducer和action中完全一致(比如大小写、拼写),否则reducer无法正确匹配action - 检查Redux DevTools连接:确认
composeWithDevTools正确包裹了中间件,store配置无误
关联性说明
media type错误会导致请求失败,进入USER_LOGIN_FAIL分支,不会触发USER_LOGIN_SUCCESS更新userInfo,因此两个问题直接相关:先解决请求格式错误,让请求成功,再验证Redux状态更新是否正常
内容的提问来源于stack exchange,提问作者Khaled
相关产品推荐
相关产品推荐

