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

无法从HTML表单向Node.js服务器传数据,报400错误求助

注册接口POST请求400错误排查与修复

问题描述

点击注册按钮后,浏览器控制台返回POST http://127.0.0.1:3000/user/register 400 (Bad Request),服务器运行正常但数据传递存在异常,相关代码如下:

带JavaScript事件监听器的HTML表单

<title>Register</title>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!--===============================================================================================-->  
    <link rel="icon" type="image/png" href="../assets/images/icons/favicon.ico"/>
    <!--===============================================================================================-->
<link rel="stylesheet" type="text/css" href="../assets/vendor/bootstrap/css/bootstrap.min.css">
    <!--===============================================================================================-->
<link rel="stylesheet" type="text/css" href="../assets/fonts/font-awesome-4.7.0/css/font-awesome.min.css">
    <!--===============================================================================================-->
    <link rel="stylesheet" type="text/css" href="../assets/vendor/animate/animate.css">
    <!--===============================================================================================-->  
    <link rel="stylesheet" type="text/css" href="../assets/vendor/css-hamburgers/hamburgers.min.css">
    <!--===============================================================================================-->
    <link rel="stylesheet" type="text/css" href="../assets/vendor/select2/select2.min.css">
    <!--===============================================================================================-->
    <link rel="stylesheet" type="text/css" href="../assets/css/util.css">
    <link rel="stylesheet" type="text/css" href="../assets/css/main.css">
    <!--===============================================================================================-->
</head>
<body>
    <div class="limiter">
        <div class="container-login100">
            <div class="wrap-login100">
                <div class="login100-pic js-tilt" data-tilt>
                    <img src="../assets/images/img-01.png" alt="IMG">
                </div>
    
                <form class="login100-form validate-form">
                    <span class="login100-form-title">
                        Register
                    </span>
    
    <div class="wrap-input100 validate-input" data-validate = "Valid username is required!!!">
                    <input class="input100" type="text" name="user" placeholder="Username">
                        <span class="focus-input100"></span>
                        <span class="symbol-input100">
                        <i class="fa fa-user" aria-hidden="true"></i>
                    </span>
                    </div>
    
      <div class="wrap-input100 validate-input" data-validate = "Valid email is required!!!">
                        <input class="input100" type="text" name="email" placeholder="Email">
                        <span class="focus-input100"></span>
                        <span class="symbol-input100">
                        <i class="fa fa-envelope" aria-hidden="true"></i>
                    </span>
                    </div>
    
            <div class="wrap-input100 validate-input" data-validate = "Password is required">
            <input class="input100" type="password" name="pass1" placeholder="Enter Password">
                        <span class="focus-input100"></span>
                        <span class="symbol-input100">
                        <i class="fa fa-lock" aria-hidden="true"></i>
                    </span>
                    </div>
    
    <div class="wrap-input100 validate-input" data-validate = "Valid password is required!!!">
          <input class="input100" type="password" name="pass2" placeholder="Confirm Password">
                        <span class="focus-input100"></span>
                        <span class="symbol-input100">
                        <i class="fa fa-lock" aria-hidden="true"></i>
                    </span>
                    </div>

                    <div class="username error">

                    </div>

                    <div class="email error">

                    </div>

                    <div class="password error">

                    </div>
                    
                    <div class="container-login100-form-btn">
                        <button name="submit" class="login100-form-btn">
                            Register
                        </button>
                    </div>
    
                    <div class="text-center p-t-12">
                        <span class="txt1">
                            Have an account? 
                        </span>
                        <a class="txt2" href="/user/login">
                            Login
                        </a>
                    </div>
                </form>
            </div>
        </div>
    </div>

    <!--===============================================================================================-->  
    <script src="../assets/vendor/jquery/jquery-3.2.1.min.js"></script>
    <!--===============================================================================================-->
        <script src="../assets/vendor/bootstrap/js/popper.js"></script>
        <script src="../assets/vendor/bootstrap/js/bootstrap.min.js"></script>
    <!--===============================================================================================-->
        <script src="../assets/vendor/select2/select2.min.js"></script>
    <!--===============================================================================================-->
        <script src="../assets/vendor/tilt/tilt.jquery.min.js"></script>
        <script>
            $('.js-tilt').tilt({
                scale: 1.1
            })
        </script>
    <!--===============================================================================================-->
        <script src="../assets/js/main.js"></script>

        <script>
            const form = document.querySelector('form');
            form.addEventListener('submit', async (e) => {
                e.preventDefault();

                const username = form.user.value;
                const email = form.email.value;
                const password = form .pass1.value;

                try {
                    const result = await fetch('/user/register', {
                        method: 'POST',
                        body: JSON.stringify({ username, email, password }),
                        headers: { 'Content-Type': 'application/json' }
                    });
                }
                catch (err) {
                    console.log(err);
                }
            })
        </script>
</body>
</html>

注册接口Controller代码

const register_get = (req, res) => {
    res.render('register')
}

const register_post = async (req, res) => {
    const { username, email, password } = req.body;

    try {
        const user = await User.create({ username, email, password });
        res.status(201).json({ errors });
    } 
    catch (err) {
        const errors = handleErrors(err);
        res.status(400).json({ errors });
    }
}

错误处理函数

const handleErrors = (err) => {
    let errors = { username: '', email: '', password: '' }

    if (err.code === 11000) {
        errors.email = 'Email has been registered already. Kindly Login in!';
        return errors;
    }

    if(err.message.includes('user validation failed')) {
        Object.values(err.errors).ForEach(({ properties }) => {
            errors[properties.path] = properties.message;
        });
    }

    return errors;
}

问题排查与修复

1. Controller成功响应时变量未定义

在register_post的try块中,res.status(201).json({ errors });里的errors未定义,会触发引用错误并进入catch块返回400。修复如下:

const register_post = async (req, res) => {
    const { username, email, password } = req.body;

    try {
        const user = await User.create({ username, email, password });
        // 返回成功信息,替换未定义的errors
        res.status(201).json({ message: '注册成功', userId: user._id });
    } 
    catch (err) {
        const errors = handleErrors(err);
        res.status(400).json({ errors });
    }
}

2. 错误处理函数方法拼写错误

Object.values(err.errors).ForEach中的ForEach应为小写开头的forEach,JavaScript数组方法区分大小写,拼写错误会导致方法不存在并抛出异常。修复如下:

const handleErrors = (err) => {
    let errors = { username: '', email: '', password: '' }

    if (err.code === 11000) {
        errors.email = '该邮箱已注册,请直接登录!';
        return errors;
    }

    if(err.message.includes('user validation failed')) {
        // 修正forEach拼写
        Object.values(err.errors).forEach(({ properties }) => {
            errors[properties.path] = properties.message;
        });
    }

    return errors;
}

3. 前端未验证两次密码一致性

HTML表单包含两次密码输入框,但前端未校验pass1和pass2是否一致,可能导致无效数据提交。同时需添加fetch响应状态检查(fetch仅在网络错误时触发catch,HTTP错误需手动判断)。修复后的前端JS:

const form = document.querySelector('form');
form.addEventListener('submit', async (e) => {
    e.preventDefault();

    const username = form.user.value;
    const email = form.email.value;
    const password = form.pass1.value;
    const confirmPassword = form.pass2.value;

    // 清空之前的错误提示
    document.querySelectorAll('.error').forEach(el => el.textContent = '');

    // 验证两次密码一致
    if (password !== confirmPassword) {
        document.querySelector('.password.error').textContent = '两次输入的密码不一致';
        return;
    }

    try {
        const result = await fetch('/user/register', {
            method: 'POST',
            body: JSON.stringify({ username, email, password }),
            headers: { 'Content-Type': 'application/json' }
        });

        // 检查HTTP响应状态
        if (!result.ok) {
            const errorData = await result.json();
            // 将错误信息显示到对应元素
            if (errorData.errors.username) {
                document.querySelector('.username.error').textContent = errorData.errors.username;
            }
            if (errorData.errors.email) {
                document.querySelector('.email.error').textContent = errorData.errors.email;
            }
            if (errorData.errors.password) {
                document.querySelector('.password.error').textContent = errorData.errors.password;
            }
            return;
        }

        // 注册成功后跳转到登录页
        window.location.href = '/user/login';
    }
    catch (err) {
        console.log(err);
    }
})

4. 额外注意项

  • 确保后端已配置express.json()中间件,否则无法解析JSON格式的请求体;
  • 检查MongoDB的User Schema是否包含username、email、password字段的验证规则,验证失败也会触发400错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:54:54