欢迎光临
我们一直在努力

从0到1创建一个完整的 Node.js 后端服务,mock数据提供接口(Express + jsonwebtoken)

下面创建一个完整的 Node.js 后端服务,提供登录和列表接口,使用 Mock 数据模拟真实接口。我们将使用 Express 框架 + jsonwebtoken 做身份验证。


📁 项目结构

backend/
├── src/
│ ├── routes/
│ │ ├── auth.js # 认证路由
│ │ └── user.js # 用户路由
│ ├── middleware/
│ │ └── auth.js # JWT 认证中间件
│ ├── utils/
│ │ └── mockData.js # Mock 数据生成
│ └── app.js # 应用入口
├── .env # 环境变量
├── package.json
└── README.md


🚀 第一步:初始化项目

# 创建项目目录
mkdir backend
cd backend

# 初始化 package.json
npm init -y

# 安装核心依赖
npm install express cors dotenv jsonwebtoken bcryptjs

# 安装开发依赖
npm install nodemon –save-dev


📦 第二步:配置 package.json

{
"name": "backend",
"version": "1.0.0",
"description": "Node.js 后端服务 – 提供登录和列表接口",
"main": "src/app.js",
"scripts": {
"start": "node src/app.js",
"dev": "nodemon src/app.js",
"test": "echo \\"Error: no test specified\\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.0"
},
"devDependencies": {
"nodemon": "^2.0.22"
}
}


🔐 第三步:环境变量配置

创建 .env 文件:

# .env
PORT=3000
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
JWT_EXPIRES_IN=7d
NODE_ENV=development


📊 第四步:生成 Mock 数据

创建 src/utils/mockData.js:

// src/utils/mockData.js
const bcrypt = require('bcryptjs')

// Mock 用户数据
const mockUsers = [
{
id: 1,
username: 'admin',
password: bcrypt.hashSync('123456', 10),
email: 'admin@example.com',
role: 'admin',
status: 1,
createTime: '2024-01-01 10:00:00',
lastLogin: '2024-01-15 14:30:00',
},
{
id: 2,
username: 'user1',
password: bcrypt.hashSync('123456', 10),
email: 'user1@example.com',
role: 'user',
status: 1,
createTime: '2024-01-02 11:00:00',
lastLogin: '2024-01-14 09:20:00',
},
{
id: 3,
username: 'user2',
password: bcrypt.hashSync('123456', 10),
email: 'user2@example.com',
role: 'user',
status: 0,
createTime: '2024-01-03 09:30:00',
lastLogin: '2024-01-10 16:45:00',
},
{
id: 4,
username: 'manager',
password: bcrypt.hashSync('123456', 10),
email: 'manager@example.com',
role: 'manager',
status: 1,
createTime: '2024-01-04 13:20:00',
lastLogin: '2024-01-13 11:10:00',
},
{
id: 5,
username: 'developer',
password: bcrypt.hashSync('123456', 10),
email: 'developer@example.com',
role: 'developer',
status: 1,
createTime: '2024-01-05 08:45:00',
lastLogin: '2024-01-12 17:00:00',
},
]

// Mock 用户列表数据(用于分页)
const generateUserList = (page = 1, size = 10, searchParams = {}) => {
// 过滤数据
let filteredUsers = […mockUsers]

// 关键词搜索(用户名或邮箱)
if (searchParams.keyword) {
const keyword = searchParams.keyword.toLowerCase()
filteredUsers = filteredUsers.filter(
user =>
user.username.toLowerCase().includes(keyword) ||
user.email.toLowerCase().includes(keyword)
)
}

// 状态过滤
if (searchParams.status !== undefined && searchParams.status !== '') {
filteredUsers = filteredUsers.filter(
user => user.status === Number(searchParams.status)
)
}

// 角色过滤
if (searchParams.role) {
filteredUsers = filteredUsers.filter(
user => user.role === searchParams.role
)
}

// 排序(按创建时间倒序)
filteredUsers.sort((a, b) => {
return new Date(b.createTime) – new Date(a.createTime)
})

// 计算总数
const total = filteredUsers.length

// 分页
const start = (page – 1) * size
const end = start + size
const rows = filteredUsers.slice(start, end)

return {
total,
rows,
page: Number(page),
size: Number(size),
totalPages: Math.ceil(total / size),
}
}

// 通过用户名查找用户
const findUserByUsername = (username) => {
return mockUsers.find(user => user.username === username)
}

// 通过 ID 查找用户
const findUserById = (id) => {
return mockUsers.find(user => user.id === Number(id))
}

module.exports = {
mockUsers,
generateUserList,
findUserByUsername,
findUserById,
}


🛡️ 第五步:JWT 认证中间件

创建 src/middleware/auth.js:

// src/middleware/auth.js
const jwt = require('jsonwebtoken')

// JWT 认证中间件
const authenticate = (req, res, next) => {
try {
// 1. 从请求头获取 token
const authHeader = req.headers.authorization

if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
code: 401,
msg: '未登录或登录已过期',
data: null,
})
}

// 2. 提取 token
const token = authHeader.substring(7) // 去掉 'Bearer '

// 3. 验证 token
const secret = process.env.JWT_SECRET || 'your-secret-key'
const decoded = jwt.verify(token, secret)

// 4. 将用户信息挂载到 request 对象
req.user = decoded

next()
} catch (error) {
console.error('JWT 验证失败:', error)

// Token 过期或无效
if (error.name === 'TokenExpiredError') {
return res.status(401).json({
code: 401,
msg: '登录已过期,请重新登录',
data: null,
})
}

return res.status(401).json({
code: 401,
msg: '无效的 Token',
data: null,
})
}
}

// 可选:角色权限校验
const authorize = (roles = []) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({
code: 401,
msg: '未登录',
data: null,
})
}

// 如果 roles 为空,表示不需要特定角色
if (roles.length === 0) {
return next()
}

// 检查用户角色是否在允许列表中
if (!roles.includes(req.user.role)) {
return res.status(403).json({
code: 403,
msg: '没有权限访问该资源',
data: null,
})
}

next()
}
}

module.exports = {
authenticate,
authorize,
}


🛣️ 第六步:创建路由

1. 认证路由 (登录)

创建 src/routes/auth.js:

// src/routes/auth.js
const express = require('express')
const router = express.Router()
const jwt = require('jsonwebtoken')
const bcrypt = require('bcryptjs')
const { findUserByUsername } = require('../utils/mockData')

/**
* 用户登录
* POST /api/auth/login
* Body: { username, password }
*/

router.post('/login', async (req, res) => {
try {
const { username, password } = req.body

// 1. 验证参数
if (!username || !password) {
return res.status(400).json({
code: 400,
msg: '用户名和密码不能为空',
data: null,
})
}

// 2. 查找用户
const user = findUserByUsername(username)
if (!user) {
return res.status(401).json({
code: 401,
msg: '用户名或密码错误',
data: null,
})
}

// 3. 验证密码
const isValidPassword = bcrypt.compareSync(password, user.password)
if (!isValidPassword) {
return res.status(401).json({
code: 401,
msg: '用户名或密码错误',
data: null,
})
}

// 4. 检查用户状态
if (user.status === 0) {
return res.status(403).json({
code: 403,
msg: '用户已被禁用,请联系管理员',
data: null,
})
}

// 5. 生成 JWT Token
const payload = {
id: user.id,
username: user.username,
email: user.email,
role: user.role,
}

const secret = process.env.JWT_SECRET || 'your-secret-key'
const expiresIn = process.env.JWT_EXPIRES_IN || '7d'

const token = jwt.sign(payload, secret, { expiresIn })

// 6. 返回登录成功信息(不包含密码)
const userInfo = {
id: user.id,
username: user.username,
email: user.email,
role: user.role,
status: user.status,
createTime: user.createTime,
lastLogin: new Date().toISOString().replace('T', ' ').substring(0, 19),
}

res.json({
code: 0,
msg: '登录成功',
data: {
token,
userInfo,
},
})

} catch (error) {
console.error('登录接口错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})

/**
* 验证 Token 是否有效
* GET /api/auth/verify
*/

router.get('/verify', (req, res) => {
try {
const authHeader = req.headers.authorization

if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
code: 401,
msg: '未登录',
data: null,
})
}

const token = authHeader.substring(7)
const secret = process.env.JWT_SECRET || 'your-secret-key'
const decoded = jwt.verify(token, secret)

res.json({
code: 0,
msg: 'Token 有效',
data: decoded,
})
} catch (error) {
res.status(401).json({
code: 401,
msg: '无效的 Token',
data: null,
})
}
})

module.exports = router

2. 用户路由 (列表)

创建 src/routes/user.js:

// src/routes/user.js
const express = require('express')
const router = express.Router()
const { authenticate, authorize } = require('../middleware/auth')
const { generateUserList, findUserById } = require('../utils/mockData')

/**
* 获取用户列表(分页)
* GET /api/user/list
* Query: { page, size, keyword, status, role }
* 需要认证
*/

router.get('/list', authenticate, (req, res) => {
try {
// 1. 获取查询参数
const page = parseInt(req.query.page) || 1
const size = parseInt(req.query.size) || 10
const keyword = req.query.keyword || ''
const status = req.query.status !== undefined ? req.query.status : ''
const role = req.query.role || ''

// 2. 参数校验
if (page < 1) {
return res.status(400).json({
code: 400,
msg: '页码不能小于 1',
data: null,
})
}

if (size < 1 || size > 100) {
return res.status(400).json({
code: 400,
msg: '每页大小必须在 1-100 之间',
data: null,
})
}

// 3. 获取数据
const result = generateUserList(page, size, {
keyword,
status: status === '' ? undefined : status,
role,
})

// 4. 返回数据(不包含密码)
const rows = result.rows.map(user => {
const { password, …userWithoutPassword } = user
return userWithoutPassword
})

res.json({
code: 0,
msg: '获取用户列表成功',
data: {
total: result.total,
rows,
page: result.page,
size: result.size,
totalPages: result.totalPages,
},
})

} catch (error) {
console.error('获取用户列表错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})

/**
* 获取用户详情
* GET /api/user/:id
* 需要认证
*/

router.get('/:id', authenticate, (req, res) => {
try {
const id = parseInt(req.params.id)

if (isNaN(id)) {
return res.status(400).json({
code: 400,
msg: '无效的用户 ID',
data: null,
})
}

const user = findUserById(id)
if (!user) {
return res.status(404).json({
code: 404,
msg: '用户不存在',
data: null,
})
}

// 移除密码
const { password, …userInfo } = user

res.json({
code: 0,
msg: '获取用户详情成功',
data: userInfo,
})

} catch (error) {
console.error('获取用户详情错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})

/**
* 获取当前用户信息
* GET /api/user/info
* 需要认证
*/

router.get('/info', authenticate, (req, res) => {
try {
const userId = req.user.id
const user = findUserById(userId)

if (!user) {
return res.status(404).json({
code: 404,
msg: '用户不存在',
data: null,
})
}

// 移除密码
const { password, …userInfo } = user

res.json({
code: 0,
msg: '获取用户信息成功',
data: userInfo,
})

} catch (error) {
console.error('获取用户信息错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})

/**
* 创建用户(需要管理员权限)
* POST /api/user
* 需要认证 + 管理员权限
*/

router.post('/', authenticate, authorize(['admin']), (req, res) => {
try {
const { username, email, role } = req.body

// 简单验证
if (!username || !email) {
return res.status(400).json({
code: 400,
msg: '用户名和邮箱不能为空',
data: null,
})
}

// 模拟创建成功
const newUser = {
id: Date.now(),
username,
email,
role: role || 'user',
status: 1,
createTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
lastLogin: null,
}

res.json({
code: 0,
msg: '创建用户成功',
data: newUser,
})

} catch (error) {
console.error('创建用户错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})

/**
* 更新用户(需要管理员权限)
* PUT /api/user/:id
* 需要认证 + 管理员权限
*/

router.put('/:id', authenticate, authorize(['admin']), (req, res) => {
try {
const id = parseInt(req.params.id)
const { email, role, status } = req.body

const user = findUserById(id)
if (!user) {
return res.status(404).json({
code: 404,
msg: '用户不存在',
data: null,
})
}

// 模拟更新成功
const updatedUser = {
…user,
email: email || user.email,
role: role || user.role,
status: status !== undefined ? status : user.status,
}

const { password, …userInfo } = updatedUser

res.json({
code: 0,
msg: '更新用户成功',
data: userInfo,
})

} catch (error) {
console.error('更新用户错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})

/**
* 删除用户(需要管理员权限)
* DELETE /api/user/:id
* 需要认证 + 管理员权限
*/

router.delete('/:id', authenticate, authorize(['admin']), (req, res) => {
try {
const id = parseInt(req.params.id)

const user = findUserById(id)
if (!user) {
return res.status(404).json({
code: 404,
msg: '用户不存在',
data: null,
})
}

// 模拟删除成功
res.json({
code: 0,
msg: '删除用户成功',
data: null,
})

} catch (error) {
console.error('删除用户错误:', error)
res.status(500).json({
code: 500,
msg: '服务器内部错误',
data: null,
})
}
})

module.exports = router


🚀 第七步:创建应用入口

创建 src/app.js:

// src/app.js
const express = require('express')
const cors = require('cors')
require('dotenv').config()

// 导入路由
const authRoutes = require('./routes/auth')
const userRoutes = require('./routes/user')

// 创建 Express 应用
const app = express()
const PORT = process.env.PORT || 3000

// 中间件
app.use(cors()) // 允许跨域
app.use(express.json()) // 解析 JSON 请求体
app.use(express.urlencoded({ extended: true })) // 解析 URL-encoded 请求体

// 日志中间件(开发环境)
if (process.env.NODE_ENV === 'development') {
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`)
next()
})
}

// 路由
app.use('/api/auth', authRoutes)
app.use('/api/user', userRoutes)

// 健康检查
app.get('/health', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
})
})

// 404 处理
app.use((req, res) => {
res.status(404).json({
code: 404,
msg: '接口不存在',
data: null,
})
})

// 全局错误处理
app.use((err, req, res, next) => {
console.error('全局错误:', err)
res.status(500).json({
code: 500,
msg: err.message || '服务器内部错误',
data: null,
})
})

// 启动服务器
app.listen(PORT, () => {
console.log(`🚀 服务器已启动`)
console.log(`📍 地址: http://localhost:${PORT}`)
console.log(`🌿 环境: ${process.env.NODE_ENV || 'development'}`)
console.log('\\n📋 接口列表:')
console.log(` POST /api/auth/login – 用户登录`)
console.log(` GET /api/auth/verify – 验证 Token`)
console.log(` GET /api/user/list – 获取用户列表`)
console.log(` GET /api/user/:id – 获取用户详情`)
console.log(` GET /api/user/info – 获取当前用户信息`)
console.log(` POST /api/user – 创建用户`)
console.log(` PUT /api/user/:id – 更新用户`)
console.log(` DELETE /api/user/:id – 删除用户`)
console.log(` GET /health – 健康检查\\n`)
})


🧪 第八步:测试接口

1. 启动服务

npm run dev

2. 使用 curl 或 Postman 测试

测试登录接口

curl -X POST http://localhost:3000/api/auth/login \\
-H "Content-Type: application/json" \\
-d '{"username": "admin", "password": "123456"}'

响应示例:

{
"code": 0,
"msg": "登录成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiIs…",
"userInfo": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"role": "admin",
"status": 1,
"createTime": "2024-01-01 10:00:00",
"lastLogin": "2024-01-15 14:30:00"
}
}
}

测试用户列表接口(需要 Token)

# 先获取 token,然后使用 token 访问
curl -X GET "http://localhost:3000/api/user/list?page=1&size=5" \\
-H "Authorization: Bearer YOUR_TOKEN_HERE"

响应示例:

{
"code": 0,
"msg": "获取用户列表成功",
"data": {
"total": 5,
"rows": [
{
"id": 5,
"username": "developer",
"email": "developer@example.com",
"role": "developer",
"status": 1,
"createTime": "2024-01-05 08:45:00",
"lastLogin": "2024-01-12 17:00:00"
}
],
"page": 1,
"size": 5,
"totalPages": 1
}
}


🔗 第九步:前端对接

更新前端的 API 配置:

// src/utils/axios.js
const service = axios.create({
baseURL: 'http://localhost:3000/api', // 修改为后端地址
timeout: 15000,
withCredentials: true,
})

前端 API 调用示例:

// src/api/modules/user.js
import request from '@/utils/axios'

// 用户登录
export function login(data) {
return request({
url: '/auth/login', // 注意:这里不需要 /api 前缀,因为 baseURL 已经包含
method: 'post',
data,
})
}

// 获取用户列表
export function getUserList(params) {
return request({
url: '/user/list',
method: 'get',
params,
})
}


📋 接口文档

接口方法URL需要认证说明
用户登录 POST /api/auth/login ❌ 用户名密码登录,返回 Token
验证 Token GET /api/auth/verify ✅ 验证 Token 是否有效
用户列表 GET /api/user/list ✅ 分页获取用户列表
用户详情 GET /api/user/:id ✅ 获取指定用户信息
当前用户信息 GET /api/user/info ✅ 获取当前登录用户信息
创建用户 POST /api/user ✅ (admin) 创建新用户
更新用户 PUT /api/user/:id ✅ (admin) 更新用户信息
删除用户 DELETE /api/user/:id ✅ (admin) 删除用户

🎯 高级功能(可选)

1. 添加日志记录

npm install morgan

// src/app.js
const morgan = require('morgan')
app.use(morgan('combined'))

2. 添加请求限流

npm install express-rate-limit

// src/app.js
const rateLimit = require('express-rate-limit')

const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 限制每个IP 100个请求
})

app.use('/api', limiter)

3. 添加 Swagger 文档

npm install swagger-jsdoc swagger-ui-express


✅ 项目特点

  • 完整认证:JWT Token 认证,包含登录、验证、权限控制。
  • 标准响应:统一的响应格式 { code, msg, data }。
  • Mock 数据:内置 Mock 数据,开箱即用。
  • 分页查询:支持分页、搜索、筛选、排序。
  • 权限控制:基于角色的访问控制 (RBAC)。
  • 错误处理:完善的错误处理和日志记录。
  • 跨域支持:使用 CORS 中间件。
  • 开发友好:使用 nodemon 热重载,开发效率高。
  • 这个后端服务可以直接用于前端开发,配合之前封装的 Axios 完美对接。所有数据都是 Mock 的,后续可以无缝替换为真实的数据库操作。

    赞(0)
    未经允许不得转载:171主机测评 » 从0到1创建一个完整的 Node.js 后端服务,mock数据提供接口(Express + jsonwebtoken)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址