用户登录/注册功能报错求助:前后端API调用问题排查
用户注册/登录功能故障排查与修复
问题概述
实现用户注册与登录功能时,点击登录或注册按钮均执行失败,控制台报错。预期流程为:用户提交信息存入数据库后,通过useNavigate Hook跳转至对应页面。后端API通过Postman测试功能正常。
环境说明
- 后端服务地址:
http://localhost:5000 - 前端服务地址:
http://localhost:3000 - 使用
concurrently同时启动前后端服务
报错情况
点击登录/注册按钮时,控制台出现请求无法到达后端的错误(如404)
后端代码
auth.js(路由文件)
// 路由1:创建新用户,POST请求:/api/auth/createuser router.post('/createuser' , [ body("name" , "请输入有效的用户名").exists(), body("email" , "请输入有效的邮箱").isEmail(), body("password" , "密码长度不能少于5位").isLength({min: 5}) ], async (req, res)=>{ let success= false; const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({success, errors: errors.array() }); } try { let user = await userDB.findOne({email: req.body.email}); if(user){ return res.status(400).json({success, message: "抱歉,该邮箱已被注册"}) } const salt = await bcrypt.genSalt(10); const password = await bcrypt.hash(req.body.password , salt); user = await userDB.create({ name: req.body.name, email:req.body.email, password: password, }) const data = { user: { id: user.id } } const authToken = jwt.sign(data , JWT_SECRET); res.json({success: true, authToken}) } catch (error) { console.error(error.message) res.status(500).send("服务器内部错误") } }); // 路由2:用户登录,POST请求:/api/auth/loginuser router.post('/loginuser', [ body("email", "请输入有效的邮箱").isEmail(), body("password", "密码长度不能少于5位").isLength({min: 5}) ], async (req, res)=>{ let success = false; const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({success , errors: errors.array() }); } const {email, password} = req.body try { let user = await userDB.findOne({email}) if(!user){ return res.status(400).json({success, message: "请输入正确的登录信息"}) } const comparePassword = await bcrypt.compare(password, user.password) if(!comparePassword){ return res.status(400).json({success, message: "请输入正确的登录信息"}) } const data = { user: { id: user.id, }, }; const authToken = jwt.sign(data, JWT_SECRET); success= true; res.json({success, authToken}) } catch (error) { console.error(error.message) res.status(500).send("服务器内部错误") } }); // 路由3:获取当前用户信息,POST请求:/api/auth/fetchuser router.post('/fetchuser' , fetchUser, async (req,res)=>{ try { userID = req.user.id const user = await userDB.findById(userID).select("-password") res.send(user); } catch (error) { console.error(error.message) res.status(500).send("服务器内部错误") } }) module.exports = router;
index.js(入口文件)
const express = require("express"); const mongoConnect = require("./db/mongoConnect"); const cors = require("cors"); const app = express(); const port = 5000; mongoConnect(); app.use(express.json()); app.use(cors()); // 挂载auth路由前缀 app.use("/api/auth" , require("./routes/auth")); app.use("/api/tasks" , require("./routes/tasks")); app.listen(port, ()=>{ console.log(`后端服务运行在端口${port}` ) })
前端代码
Login.js组件(MaterialUI)
export default function Login (){ // 修正:指向后端服务地址 const host = "http://localhost:5000"; const onChange=(e)=>{ // 修正:移除多余的数组包裹,直接存字符串值 setCredentials({...credentials, [e.target.name]: e.target.value}) } const navigate = useNavigate(); const [credentials, setCredentials] = useState({ email: "", password: "" }); const handleLogin = async (e) => { e.preventDefault(); try { const response = await fetch(`${host}/api/auth/loginuser`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: credentials.email, password: credentials.password, }), }); if (!response.ok) throw new Error(`请求失败,状态码:${response.status}`); const json = await response.json(); console.log(json); if (json.success) { // 修正:与后端返回字段名保持一致(authToken) localStorage.setItem("token", json.authToken); navigate("/"); } else { console.log(json.message || "登录信息无效"); } } catch (err) { console.error("登录请求出错:", err); } }; return( <Box component="form" noValidate onSubmit={handleLogin} sx={{ mt: 1 }} > <TextField margin="normal" type="email" required fullWidth id="email" onChange={onChange} value={credentials.email} label="邮箱地址" name="email" autoComplete="email" autoFocus /> <TextField margin="normal" required value={credentials.password} onChange={onChange} fullWidth name="password" label="密码" type="password" id="password" autoComplete="current-password" /> <Button type="submit" fullWidth variant="contained" sx={{ mt: 3, mb: 2, bgcolor: "#4c3c3c", "&:hover": { bgcolor: "#5c3c3c" }, }} > 登录 </Button> </Box> ) }
SignUp.js组件(MaterialUI)
export default function SignUp() { // 修正:指向后端服务地址 const host = "http://localhost:5000/" const [credentials, setCredentials] = useState({name: "",email: "", password: ""}) const navigate = useNavigate(); const handleSignUp = async (e) => { e.preventDefault(); try { const response = await fetch(`${host}api/auth/createuser`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: credentials.name, email: credentials.email, password: credentials.password }) }); if (!response.ok) throw new Error(`请求失败,状态码:${response.status}`); const json = await response.json(); console.log(response) if(json.success){ // 修正:与后端返回字段名保持一致(authToken) localStorage.setItem("token" , json.authToken) navigate("/login") } else { console.log(json.message || "注册信息无效") } } catch (err) { console.error("注册请求出错:", err); } }; const onChange = (e) =>{ setCredentials({...credentials, [e.target.name]: e.target.value}) } return( <Box component="form" noValidate onSubmit={handleSignUp} sx={{ mt: 3 }}> <Grid container spacing={2}> <Grid item xs={12}> <TextField required fullWidth id="name" label="用户名" onChange={onChange} name="name" autoComplete="family-name" /> </Grid> <Grid item xs={12}> <TextField required fullWidth id="email" onChange={onChange} label="邮箱地址" name="email" autoComplete="email" /> </Grid> <Grid item xs={12}> <TextField required fullWidth name="password" label="密码" onChange={onChange} type="password" id="password" autoComplete="new-password" /> </Grid> </Grid> <Button type="submit" fullWidth variant="contained" sx={{ mt: 3, mb: 2, bgcolor:"#4c3c3c" , "&:hover" :{bgcolor:"#5c3c3c"} }} > 注册 </Button> </Box> ) }
核心问题与修复总结
- API请求地址错误:前端将请求指向了前端服务
3000端口,需修正为后端5000端口地址 - 前端输入值存储错误:Login.js中把输入值存为数组,导致后端校验失败,需改为直接存储字符串
- 后端路由路径错误:createuser路由缺少开头斜杠,且因已挂载
/api/auth前缀,路由只需写/createuser - 后端响应格式不一致:部分错误场景返回字符串,需统一返回JSON格式
- 字段名拼写不一致:前端存储token时误用
authtoken,需与后端返回的authToken保持一致
内容的提问来源于stack exchange,提问作者Harshit Sharma
相关产品推荐
相关产品推荐

