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

用户登录/注册功能报错求助:前后端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>
  )
}

核心问题与修复总结

  1. API请求地址错误:前端将请求指向了前端服务3000端口,需修正为后端5000端口地址
  2. 前端输入值存储错误:Login.js中把输入值存为数组,导致后端校验失败,需改为直接存储字符串
  3. 后端路由路径错误:createuser路由缺少开头斜杠,且因已挂载/api/auth前缀,路由只需写/createuser
  4. 后端响应格式不一致:部分错误场景返回字符串,需统一返回JSON格式
  5. 字段名拼写不一致:前端存储token时误用authtoken,需与后端返回的authToken保持一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 21:40:38