添加控制器后React登录表单向后端传空数据,求排查
问题:添加控制器后登录请求后端收到空数据
原本正常运行的前后端登录系统,在添加控制器优化代码结构后,用户登录时后端接收到空数据对象,无法完成登录流程。
API 代码
index.js
const express = require('express'); const cors = require("cors"); const dotenv = require("dotenv"); const bodyParser = require('body-parser'); dotenv.config(); const app = express(); const PORT = process.env.PORT || 3000 app.use(cors()); app.use(express.urlencoded({ extended: true })) app.use(bodyParser.json()); app.get('/', (req, res) => { res.send('Testing') }) app.use("/api/auth", require("./routes/authRoutes")); app.listen(PORT, () => { console.log("app is running!") });
authRoutes.js
const express = require('express'); const router = express.Router(); const authControllers = require('../controllers/authControllers'); router.post('/register', authControllers.register); router.post('/login', authControllers.login); // router.post("/logout"); module.exports = router
authController.js
const { PrismaClient } = require('@prisma/client'); const express = require("express"); const router = express.Router(); const prisma = new PrismaClient(); const jwt = require('jsonwebtoken'); const bcrypt = require('bcrypt'); const login = async (req, res) => { console.log(req.body) try { const { email, password } = req.body; const user = await prisma.user.findUnique({ where: { email: email } }); if (!user) {return res.status(401).json({ error: "Password or email is incorrect." });} const isValidPassword = bcrypt.compareSync(password, user.password); if (!isValidPassword) {return res.status(401).json({ error: "Password or email is incorrect." });} const accesToken = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: "3h" }); res.status(200).json({ status: "OK", message: "Login succes", token: accesToken, user }); } catch (error) { console.error(error); res.status(401).json({ error: "Password or email is incorrect." }); } }; const register = async (req, res) => { try { const { username, password, email } = req.body; const existingUser = await prisma.user.find({ where: { email } }); if (existingUser) { return res.status(409).json({ error: "This email is already registered." }) } const hashedPassword = await bcrypt.hash(password, 10); const newUser = await prisma.user.create({ data: { username, password: hashedPassword, email, }, }); const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: "3h" }); res.json({ token }); } catch (error) { console.error(error); res.status(500).json({ error: "Internal server error." }); } }; module.exports = { register, login };
APP 代码
loginform.js
/* eslint-disable no-unused-vars */ /* eslint-disable react-hooks/exhaustive-deps */ import './login.css' import InputField from '../InputField'; import Button from '../Button'; import { useEffect, useState } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import ErrorField from '../ErrorField'; import useLogin from '../../../hooks/useLogin'; const LoginForm = () => { // Login component const [disable, setDisable] = useState(true); const { login, error, formValues, setFormValues } = useLogin() const handleChange = async (e) => { setFormValues({ ...formValues, [e.currentTarget.name]: e.currentTarget.value }) } const submitLoginForm = async (e) => { e.preventDefault(); await login(); } useEffect(() => { setDisable(!(formValues.email && formValues.password)) }, [formValues.password, formValues.email]); return ( <div className="login"> <img src="./logo.svg" /> <h2>Login</h2> {error && <ErrorField className="form-feedback show" value={error} />} <InputField placeholder="Email" className="form-input" name="email" type="text" value={formValues.email || ''} onChange={handleChange} /> <InputField placeholder="Password" className="form-input" name="password" type="password" value={formValues.password || ''} onChange={handleChange} /> <Button type="submit" input="LOG IN" className={`form-button ${disable ? 'disabled' : ''}`} disabled={disable} onClick={submitLoginForm}/> <Link to='/signup' className='signup-link'>No account? Sign up here!</Link> </div> ) } export default LoginForm;
useLoginHook.js
import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import axios from "../config/axios"; import useAuthContext from "./useAuthContext"; const useLogin = () => { const [error, setError] = useState(null) const [isLoading, setIsLoading] = useState(null) const { dispatch } = useAuthContext() const [formValues, setFormValues] = useState({ email: "", password: "", }) const emptyFormValues = { email: "", password: "", } const navigate = useNavigate() const login = async (formValues) => { setIsLoading(true) setError(null) try { const response = await axios.post('api/auth/login', formValues); console.log(response.data); localStorage.setItem("token", response.data.token) dispatch({type: 'LOGIN', payload: response}) setIsLoading(false) navigate("/"); } catch (error) { console.log(error.response.data) setFormValues(emptyFormValues) setIsLoading(false) setError(error.response.data.error) } } return { login, isLoading, error, setError, formValues, setFormValues } } export default useLogin;
问题排查与解决
核心问题:前端未传递表单数据到登录函数
在useLoginHook.js中,login函数定义时声明了接收formValues参数,但在loginform.js的submitLoginForm函数里调用login()时没有传入该参数,导致login函数内的formValues为undefined,axios发送的请求体为空,后端自然收到空对象。
修复步骤:
- 修改
loginform.js中的submitLoginForm函数,调用login时传入formValues:
const submitLoginForm = async (e) => { e.preventDefault(); await login(formValues); // 传入表单数据 }
- 可选优化:为避免参数遗漏,可将
useLoginHook.js中的login函数改为直接使用内部的formValues,无需外部传参:
const login = async () => { // 移除参数 setIsLoading(true) setError(null) try { const response = await axios.post('api/auth/login', formValues); // 使用内部的formValues // ... 其余代码不变 } catch (error) { // ... 其余代码不变 } }
这样修改后,loginform.js中的await login()调用就无需传参,逻辑更清晰。
额外发现的后端问题
authController.js的register函数中,生成JWT时使用了未定义的user变量,应该改为刚创建的newUser:
const token = jwt.sign({ userId: newUser.id }, process.env.JWT_SECRET, { expiresIn: "3h" });
内容的提问来源于stack exchange,提问作者Stef Verniers
相关产品推荐
相关产品推荐

