如何为用户管理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:获取所有角色列表
二、搭建后端项目
- 初始化项目
mkdir user-management-backend cd user-management-backend npm init -y
- 安装依赖
npm install express cors npm install -D nodemon
- 在
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()); };
五、测试与扩展
- 启动后端服务:
npm run dev - 启动前端服务,测试所有功能是否正常运行
- 后续可扩展方向:
- 接入真实数据库(如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
相关产品推荐
相关产品推荐

