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

POST新增用户后PUT请求误删新用户问题求助

Node.js用户注册后PUT修改数据导致新用户被删除的问题

POST请求注册新用户时,数据能正常写入users.json,但后续发起PUT请求修改该用户数据时,刚新增的用户会被删除。已注意到注册后JSON文件变成一行,但不认为是问题,猜测PUT请求可能读取了旧的用户列表,寻求解决办法。

相关代码

前端PUT请求代码

//Changing user Data
export async function changeData(id, body) {
    try {
        await axios.put(`http://localhost:8000/users/${id}`, body, {
            headers: {
                'Content-Type': 'application/json',
                'Authorization': localStorage.getItem('auth._token.local')
            }
        });
        return true;
    }
    catch (e) {
        return false;
    }
}

后端注册新用户的POST接口代码

// Register New User
server.post('/register', (req, res) => {
  console.log("register with request body", req.body)
  const {username, password, firstname, lastname, roles} = req.body

  if(!username || !password || !firstname || !lastname || !roles) {
    const status = 400
    const message = "Bad Request, make sure all properties are set in request body"
    res.status(status).json({status, message})
    return
  }

  if (req.headers.authorization === undefined || req.headers.authorization.split(' ')[0] !== 'Bearer') {
    const status = 401
    const message = 'Error in authorization format'
    res.status(status).json({status, message})
    return
  }

  // Send only token part to admin check
  if(!isAdmin(req.headers.authorization.split(' ')[1])) {
    const status = 401
    const message = 'Only permitted by admin'
    res.status(status).json({status, message})
    return
  }

  if(isAuthenticated({username, password}) === true) {
    const status = 401
    const message = 'Email and Password already exist'
    res.status(status).json({status, message})
    return
  }

  fs.readFile("./users.json", (err, file) => {
      if (err) {
        const status = 401
        const message = err
        res.status(status).json({status, message})
        return
      }

      // Get current users data
      const data = JSON.parse(file.toString())

      // Get the id of last user
      const last_item_id = data.users[data.users.length - 1].id

      //Add new user
      data.users.push({id: last_item_id + 1, username: username, password: password, firstname: firstname, lastname: lastname, roles: roles}) //add some data
      const writeData = fs.writeFile("./users.json", JSON.stringify(data), (err, result) => {  // WRITE
          if (err) {
            const status = 401
            const message = err
            res.status(status).json({status, message})
          } else {
            // 文件写入成功后再返回响应
            res.status(201).json({status: 201, message: "Successfully created"})
          }
      })
  })

  // 原代码这里直接返回响应,此时文件可能还未写入完成
  // res.status(201).json({status: 201, message: "Successfully created"})
})

后端处理用户数据修改的中间件代码

// handle changing user data
server.use((req, res, next) => {
  console.log('Entering Users')
  if(req.method === 'PUT' && req.url.includes("/users")) {
    if(req.body) {
      const decodedToken = jwt.decode(req.headers.authorization.split(' ')[1])
      const userList = JSON.parse(fs.readFileSync('./users.json', 'UTF-8'))
      const userinfo = userList.users.find((user) => user.id === decodedToken.id)
     
      if(!req.body.password) {
        req.body.password = userinfo.password
      }

      // if admin made the request, he should be able to change roles
      if(req.body.roles && decodedToken.roles && decodedToken.roles.includes("admin")) {
        console.log("Able to change");
        next()
        return
      }
      req.body.roles = decodedToken.roles
    } else {
      res.status(400).json(
        {
          status: 400,
          message: "Bad request, make sure all properties are set in request body"
        }
      )
      
      return
    }
  }
  next()
})

问题原因与解决方案

核心问题

注册接口中,fs.writeFile是异步操作,但原代码在调用fs.writeFile后立即返回了201响应。此时文件可能还未完成写入,客户端收到成功响应后立刻发起PUT请求,中间件通过fs.readFileSync读取的仍是旧的文件内容(新用户尚未写入)。后续PUT路由处理时,会基于旧列表修改并写入文件,导致刚注册的用户被覆盖删除。

修复步骤

  1. 调整注册接口的响应时机:将返回201响应的代码移到fs.writeFile的成功回调中,确保文件写入完成后再通知客户端。修改后的代码已在上面的POST接口中标记。

  2. 建议使用Promise版fs API:改用fs.promises.readFile和fs.promises.writeFile,配合async/await语法,避免回调嵌套,代码更易维护:

// 改写后的注册接口(async/await版本)
server.post('/register', async (req, res) => {
  console.log("register with request body", req.body)
  const {username, password, firstname, lastname, roles} = req.body

  // 省略前面的校验逻辑...

  try {
    const file = await fs.promises.readFile("./users.json");
    const data = JSON.parse(file.toString());
    const last_item_id = data.users[data.users.length - 1].id;
    data.users.push({id: last_item_id + 1, username, password, firstname, lastname, roles});
    await fs.promises.writeFile("./users.json", JSON.stringify(data));
    res.status(201).json({status: 201, message: "Successfully created"});
  } catch (err) {
    res.status(401).json({status: 401, message: err});
  }
})
  1. 检查PUT路由逻辑:确保PUT路由在修改用户时,是找到对应ID的用户并更新其属性,而非错误地替换整个用户数组。示例逻辑如下:
server.put('/users/:id', async (req, res) => {
  const userId = parseInt(req.params.id);
  try {
    const file = await fs.promises.readFile("./users.json");
    const data = JSON.parse(file.toString());
    const userIndex = data.users.findIndex(user => user.id === userId);
    if (userIndex === -1) {
      return res.status(404).json({status:404, message: "User not found"});
    }
    // 合并请求体和原有用户数据,保留未修改的字段
    data.users[userIndex] = {...data.users[userIndex], ...req.body};
    await fs.promises.writeFile("./users.json", JSON.stringify(data));
    res.status(200).json({status:200, message: "User updated successfully"});
  } catch (err) {
    res.status(500).json({status:500, message: err});
  }
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 10:35:17