Node.jsExpress服务器 nodejs-express-server

构建健壮的 Express.js 应用程序,包括路由、中间件、认证和数据库集成,适用于创建 RESTful API 和服务器端请求处理。

后端开发 0 次安装 0 次浏览 更新于 3/4/2026

Node.js Express 服务器

概述

创建健壮的 Express.js 应用程序,遵循行业最佳实践,包括适当的路由、中间件链、认证机制和数据库集成。

使用场景

  • 使用 Node.js 构建 REST API
  • 实现服务器端请求处理
  • 创建中间件链以处理跨领域关注点
  • 管理认证和授权
  • 从 Node.js 连接到数据库
  • 实现错误处理和日志记录

指南

1. 基本 Express 设置

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// 路由
app.get('/health', (req, res) => {
  res.json({ status: 'OK', timestamp: new Date().toISOString() });
});

// 错误处理
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(err.status || 500).json({
    error: err.message,
    requestId: req.id
  });
});

app.listen(PORT, () => {
  console.log(`服务器在端口 ${PORT} 上运行`);
});

2. 中间件链实现

// 日志记录中间件
const logger = (req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.path} ${res.statusCode} ${duration}ms`);
  });
  next();
};

// 认证中间件
const authenticateToken = (req, res, next) => {
  const token = req.headers['authorization']?.split(' ')[1];
  if (!token) return res.status(401).json({ error: '无令牌' });

  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.status(403).json({ error: '无效令牌' });
    req.user = user;
    next();
  });
};

// 错误捕获中间件包装器
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

app.use(logger);
app.use(express.json());
app.get('/protected', authenticateToken, (req, res) => {
  res.json({ user: req.user });
});

3. 数据库集成(PostgreSQL 与 Sequelize)

const { Sequelize, DataTypes } = require('sequelize');

const sequelize = new Sequelize(
  process.env.DB_NAME,
  process.env.DB_USER,
  process.env.DB_PASS,
  {
    host: process.env.DB_HOST,
    dialect: 'postgres',
    logging: false
  }
);

const User = sequelize.define('User', {
  id: {
    type: DataTypes.UUID,
    defaultValue: DataTypes.UUIDV4,
    primaryKey: true
  },
  email: {
    type: DataTypes.STRING,
    unique: true,
    allowNull: false
  },
  password: DataTypes.STRING,
  role: {
    type: DataTypes.ENUM('user', 'admin'),
    defaultValue: 'user'
  }
}, {
  timestamps: true
});

// 同步数据库
sequelize.sync({ alter: true });

4. JWT 认证

const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');

const generateToken = (userId) => {
  return jwt.sign(
    { userId, iat: Math.floor(Date.now() / 1000) },
    process.env.JWT_SECRET,
    { expiresIn: '24h' }
  );
};

app.post('/login', asyncHandler(async (req, res) => {
  const { email, password } = req.body;
  const user = await User.findOne({ where: { email } });

  if (!user) return res.status(404).json({ error: '用户未找到' });

  const validPassword = await bcrypt.compare(password, user.password);
  if (!validPassword) return res.status(401).json({ error: '密码无效' });

  const token = generateToken(user.id);
  res.json({ token, user: { id: user.id, email: user.email } });
}));

5. RESTful 路由与 CRUD 操作

const userRouter = express.Router();

// 获取所有用户(带分页)
userRouter.get('/', authenticateToken, asyncHandler(async (req, res) => {
  const { page = 1, limit = 20 } = req.query;
  const users = await User.findAndCountAll({
    offset: (page - 1) * limit,
    limit: parseInt(limit)
  });

  res.json({
    data: users.rows,
    pagination: { page, limit, total: users.count }
  });
}));

// 获取单个用户
userRouter.get('/:id', authenticateToken, asyncHandler(async (req, res) => {
  const user = await User.findByPk(req.params.id);
  if (!user) return res.status(404).json({ error: '未找到' });
  res.json({ data: user });
}));

// POST 创建用户
userRouter.post('/', asyncHandler(async (req, res) => {
  const { email, password } = req.body;
  const hashedPassword = await bcrypt.hash(password, 10);

  const user = await User.create({
    email,
    password: hashedPassword
  });

  res.status(201).json({ data: user });
}));

// PATCH 更新用户
userRouter.patch('/:id', authenticateToken, asyncHandler(async (req, res) => {
  const user = await User.findByPk(req.params.id);
  if (!user) return res.status(404).json({ error: '未找到' });

  await user.update(req.body, {
    fields: ['email', 'role']
  });

  res.json({ data: user });
}));

// DELETE 用户
userRouter.delete('/:id', authenticateToken, asyncHandler(async (req, res) => {
  const user = await User.findByPk(req.params.id);
  if (!user) return res.status(404).json({ error: '未找到' });

  await user.destroy();
  res.status(204).send();
}));

app.use('/api/users', userRouter);

6. 错误处理中间件

class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    Error.captureStackTrace(this, this.constructor);
  }
}

app.use((err, req, res, next) => {
  err.statusCode = err.statusCode || 500;

  if (err.name === 'SequelizeValidationError') {
    return res.status(400).json({
      error: '验证失败',
      details: err.errors.map(e => ({ field: e.path, message: e.message }))
    });
  }

  if (process.env.NODE_ENV === 'production') {
    return res.status(err.statusCode).json({
      error: err.message,
      requestId: req.id
    });
  }

  res.status(err.statusCode).json({
    error: err.message,
    stack: err.stack
  });
});

app.use((req, res) => {
  res.status(404).json({ error: '路由未找到' });
});

7. 环境配置

require('dotenv').config();

const config = {
  port: process.env.PORT || 3000,
  env: process.env.NODE_ENV || 'development',
  database: {
    url: process.env.DATABASE_URL,
    dialect: 'postgres'
  },
  jwt: {
    secret: process.env.JWT_SECRET,
    expiresIn: '24h'
  },
  cors: {
    origin: process.env.CORS_ORIGIN || 'http://localhost:3000'
  }
};

module.exports = config;

最佳实践

✅ 应该做

  • 使用中间件处理跨领域关注点
  • 实现适当的错误处理
  • 在处理之前验证输入数据
  • 使用 async/await 进行异步操作
  • 在受保护的路由上实现认证
  • 使用环境变量进行配置
  • 添加日志记录和监控
  • 在生产中使用 HTTPS
  • 实现速率限制
  • 保持路由处理程序专注且小巧

❌ 不应该做

  • 默默处理错误
  • 在代码中存储敏感数据
  • 在路由中使用同步操作
  • 忘记验证用户输入
  • 在路由处理程序中实现认证
  • 使用回调地狱(使用 promises/async-await)
  • 在生产中暴露堆栈跟踪
  • 仅信任客户端验证

完整示例

const express = require('express');
const jwt = require('jsonwebtoken');
const { Sequelize, DataTypes } = require('sequelize');

const app = express();
app.use(express.json());

const sequelize = new Sequelize('postgres://user:pass@localhost/db');

const User = sequelize.define('User', {
  email: DataTypes.STRING,
  password: DataTypes.STRING
});

const authenticateToken = (req, res, next) => {
  const token = req.headers['authorization']?.split(' ')[1];
  jwt.verify(token, 'secret', (err, user) => {
    if (err) return res.status(403).json({ error: '禁止' });
    req.user = user;
    next();
  });
};

app.get('/users', authenticateToken, async (req, res, next) => {
  try {
    const users = await User.findAll();
    res.json({ data: users });
  } catch (err) {
    next(err);
  }
});

app.post('/users', async (req, res, next) => {
  try {
    const user = await User.create(req.body);
    res.status(201).json({ data: user });
  } catch (err) {
    next(err);
  }
});

app.listen(3000, () => console.log('服务器在端口 3000 上运行'));