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

如何为用户管理SPA开发对应的后端API?

如何为React用户管理SPA开发配套后端API

你已经完成具备用户查看、创建、编辑、删除功能的React前端,以下是针对该前端的后端API开发方案(以Node.js + Express为例):

一、分析前端所需API接口

从你的前端代码中,可提取出核心接口需求:

用户相关接口

  • GET /api/users:获取所有用户列表
  • GET /api/users/:id:根据ID获取单个用户详情
  • POST /api/users:创建新用户
  • PUT /api/users/:id:更新指定用户信息
  • DELETE /api/users/:id:删除指定用户

角色相关接口

  • GET /api/roles:获取所有角色列表

二、搭建后端项目

  1. 初始化项目
mkdir user-management-backend
cd user-management-backend
npm init -y
  1. 安装依赖
npm install express cors
npm install -D nodemon
  1. 在package.json中添加启动脚本:
"scripts": {
  "start": "node server.js",
  "dev": "nodemon server.js"
}

三、编写后端核心代码

创建server.js文件,内容如下:

const express = require('express');
const cors = require('cors');

const app = express();
const PORT = 5000;

// 启用CORS,允许前端跨域请求
app.use(cors());
// 解析JSON请求体
app.use(express.json());

// 模拟数据库数据
let users = [
  { id: 1, name: '张三', roleId: 1 },
  { id: 2, name: '李四', roleId: 2 }
];

let roles = [
  { id: 1, title: '管理员' },
  { id: 2, title: '普通用户' },
  { id: 3, title: '访客' }
];

// 用户接口
// 获取所有用户
app.get('/api/users', (req, res) => {
  // 关联角色ID返回
  const usersWithRole = users.map(user => ({
    ...user,
    role: user.roleId
  }));
  res.json(usersWithRole);
});

// 获取单个用户
app.get('/api/users/:id', (req, res) => {
  const userId = parseInt(req.params.id);
  const user = users.find(u => u.id === userId);
  if (!user) {
    return res.status(404).json({ message: '用户不存在' });
  }
  res.json({ ...user, role: user.roleId });
});

// 创建用户
app.post('/api/users', (req, res) => {
  const { name, role } = req.body;
  if (!name || !role) {
    return res.status(400).json({ message: '用户名和角色不能为空' });
  }
  const newUser = {
    id: users.length + 1,
    name,
    roleId: parseInt(role)
  };
  users.push(newUser);
  res.status(201).json(newUser);
});

// 更新用户
app.put('/api/users/:id', (req, res) => {
  const userId = parseInt(req.params.id);
  const { name, role } = req.body;
  const userIndex = users.findIndex(u => u.id === userId);
  if (userIndex === -1) {
    return res.status(404).json({ message: '用户不存在' });
  }
  users[userIndex] = {
    ...users[userIndex],
    name,
    roleId: parseInt(role)
  };
  res.json(users[userIndex]);
});

// 删除用户
app.delete('/api/users/:id', (req, res) => {
  const userId = parseInt(req.params.id);
  users = users.filter(u => u.id !== userId);
  res.json({ message: '用户删除成功' });
});

// 角色接口
// 获取所有角色
app.get('/api/roles', (req, res) => {
  res.json(roles);
});

app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`);
});

四、对接前端服务

修改前端的user.service.js和role.service.js,将请求地址指向后端API:

user.service.js示例

const BASE_URL = 'http://localhost:5000/api/users';

export const getUsers = () => {
  return fetch(BASE_URL)
    .then(res => res.json());
};

export const getUser = (id) => {
  return fetch(`${BASE_URL}/${id}`)
    .then(res => res.json());
};

export const createUser = (user) => {
  return fetch(BASE_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(user)
  })
  .then(res => res.json());
};

export const updateUser = (user) => {
  return fetch(`${BASE_URL}/${user.id}`, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(user)
  })
  .then(res => res.json());
};

export const deleteUser = (user) => {
  return fetch(`${BASE_URL}/${user.id}`, {
    method: 'DELETE'
  })
  .then(res => res.json());
};

role.service.js示例

const BASE_URL = 'http://localhost:5000/api/roles';

export const getRoles = () => {
  return fetch(BASE_URL)
    .then(res => res.json());
};

五、测试与扩展

  1. 启动后端服务:npm run dev
  2. 启动前端服务,测试所有功能是否正常运行
  3. 后续可扩展方向:
    • 接入真实数据库(如MySQL、MongoDB)
    • 添加用户认证与授权
    • 增强输入验证和错误处理
    • 添加日志记录

前端代码参考

UserWithEdit组件

import { useEffect, useState } from "react"
import { getRoles } from "../services/role.service"
import { updateUser } from "../services/user.service"

/* eslint-disable react/prop-types */
export const User = ({ user, onDelete }) => {
    const [name, setName] = useState(user.name)
    const [roleId, setRoleId] = useState(user.roleId)

    const [roles, setRoles] = useState([])
    const [edit, setEdit] = useState(false)

    const handleNameChange = (_name) => {
        setName(_name)
        user.name = _name
    }

    const handleRoleChange = (_roleId) => {
        setRoleId(_roleId)
        user.roleId = parseInt(_roleId)
    }

    const save = () => {
        updateUser(user)
        setEdit(false)
    }

    useEffect(() => {
        getRoles()
        .then(res => {
            setRoles(res)
        })
    },[])
    
    return (
        <tr>
                <td>{user.id}</td>
                <td>
                    { edit 
                        ? <input className="form-control" type="text" id="name" value={name} onChange={(event) => handleNameChange(event.target.value)}/>
                        : <span className="me-2">{name}</span>
                    }
                </td>
                <td>
                    <select className="form-control form-control-sm" name="roles" id="roles" value={roleId} onChange={(event) => handleRoleChange(event.target.value)} disabled={!edit}>
                        {roles.map(role => <option key={role.id} value={role.id}>{role.title}</option>)}
                    </select>
                </td>
                <td>
                    <button className="btn btn-outline-danger me-2" onClick={() => onDelete(user)}>X</button>
                    {
                        edit
                        ? <button className="btn btn-outline-success" onClick={() => save()}><i className="bi bi-check"></i></button>
                        : <button className="btn btn-outline-primary" onClick={() => setEdit(true)}><i className="bi bi-pencil"></i></button>
                    }
                </td>
        </tr>
    )
}

Navbar组件

export const NavigationBar = () => {
    return (
        <nav className="navbar navbar-light bg-light">
            <div className="container-fluid">
                <a className="navbar-brand" href="/">
                    <i className="bi-person me-2"></i>
                    User-Administration
                </a>
            </div>
        </nav>
    )
}

User组件

import { useEffect, useState } from "react";
import { getRoles } from "../services/role.service";
import { useNavigate } from "react-router-dom";

/* eslint-disable react/prop-types */
export const User = ({ user, onDelete }) => {
    const [roles, setRoles] = useState([])

    let navigate = useNavigate();

    useEffect(() => {
        getRoles()
        .then(res => {
            setRoles(res)
        })
    },[])
    
    return (
        <tr>
                <td>{user.id}</td>
                <td>
                    <span className="me-2">{user.name}</span>
                </td>
                <td>
                    <select className="form-control form-control-sm" name="roles" id="roles" value={user.role} disabled>
                        {roles.map(role => <option key={role.id} value={role.id}>{role.title}</option>)}
                    </select>
                </td>
                <td>
                    <button className="btn btn-outline-danger me-2" onClick={() => onDelete(user)}>X</button>
                    <button className="btn btn-outline-primary" onClick={() => navigate(`/edit/${user.id}`)}><i className="bi bi-pencil"></i></button>
                </td>
        </tr>
    )
}

UserEdit组件

import { useEffect, useState } from "react";
import { createUser, getUser, updateUser } from "../services/user.service";
import { useNavigate, useParams } from "react-router-dom";
import { getRoles } from "../services/role.service";

export const UserEdit = () => {
  const [name, setName] = useState("");
  const [role, setRole] = useState("");

  const [roles, setRoles] = useState([]);

  let navigate = useNavigate();
  const params = useParams();

  const handleSubmit = (event) => {
    event.preventDefault();

    if (name != "") {
      if(params.id) {
        const user = { id: params.id, name, role };
        updateUser(user).then()
      } else {
        const user = { name, role };
        createUser(user).then()
      }

      navigate("/");
    }
  };

  useEffect(() => {
    getRoles().then((res) => {
      setRoles(res);
    });

    if (params.id) {
      getUser(params.id).then((user) => {
        setName(user.name)
        setRole(user.role)
      });
    }
  }, []);

  return (
    <>
      <h4>{params.id ? 'Edit' : 'New'} User</h4>
      <div className="card">
        <div className="card-body">
          <form onSubmit={handleSubmit}>
            <label htmlFor="name">Name</label>
            <input
              className="form-control"
              type="text"
              id="name"
              value={name}
              onChange={(event) => setName(event.target.value)}
              placeholder="Fill in Name"
              required
            />

            <label htmlFor="role">Rolle</label>
            <select
              className="form-select mb-3"
              name="role"
              id="role"
              value={role}
              onChange={(event) => setRole(event.target.value)}
              required
            >
              <option value="">Select Role</option>
              {roles.map((role) => (
                <option key={role.id} value={role.id}>
                  {role.title}
                </option>
              ))}
            </select>

            <button className="btn btn-outline-primary me-2" type="submit">
              Save
            </button>
            <button className="btn btn-outline-secondary" onClick={() => navigate(-1)} type="button">
              Cancel
            </button>
          </form>
        </div>
      </div>
    </>
  );
};

Users组件

import { useEffect, useState } from "react"
import { deleteUser, getUsers } from "../services/user.service"
import { User } from "./User"
import { useNavigate } from "react-router-dom"

export const Users = () => {
    const [users, setUsers] = useState([])

    let navigate = useNavigate();

    const handleDeleteUser = (user) => {
        deleteUser(user)
        .then(() => {
            loadUsers()
        })
    }

    const loadUsers = () => {
        getUsers()
        .then(res => {
            setUsers(res)
        })
    }

    useEffect(() => {
        loadUsers()
    },[])
    
    return (
        <>
            <div className="d-flex justify-content-between mb-3">
                <h4>List of Users</h4>
                <button className="btn btn-outline-secondary" onClick={() => navigate('/new')}>+</button>
            </div>
            <div className="card">
                <div className="card-body">
                    <table className="table">
                        <thead>
                            <tr>
                                <th>#</th>
                                <th>Username</th>
                                <th>Role</th>
                                <th>Actions</th>
                            </tr>
                        </thead>

                        <tbody className="users">
                            {users.map(user => (
                                <User key={user.id} user={user} onDelete={handleDeleteUser} />
                            ))}
                        </tbody>
                    </table>
                </div>
            </div>
        </>
    )
}

App.jsx

import './App.css'
import { Users } from './components/Users'
import { UserEdit } from './components/UserEdit'
import { Route, Routes } from 'react-router-dom'
import { NavigationBar } from './components/Navbar'

function App() {
  return (
    <>
      <NavigationBar />
      <div className="container mt-3">
        <Routes>
          <Route path="/" element={<Users />} />
          <Route path="/new" element={<UserEdit />} />
          <Route path="/edit/:id" element={<UserEdit />} />
        </Routes>
      </div>
    </>
  )
}

export default App

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 18:07:28