跳到主要内容

Express

Express 是一个轻量级、灵活的 Node.js Web 框架,以其简单易用和高度可扩展著称。它提供了简洁的 API 设计,支持中间件机制,能快速构建 RESTful API 或全栈应用。Express 拥有丰富的插件生态,可轻松集成数据库、身份验证等功能,同时保持高性能和低学习成本,是 Node.js 开发者的首选框架之一。

本指南介绍如何在 CloudBase HTTP 云函数上部署 Express 应用程序。

前置条件

在开始之前,请确保您已经:

  • 安装了 Node.js 18.x 或更高版本
  • 拥有腾讯云账号并开通了 CloudBase 服务
  • 了解基本的 Node.js 和 Express 开发知识

第一步:创建 Express 应用

💡 提示:如果您已经有一个 Express 应用,可以跳过此步骤。

创建项目目录

mkdir express-cloudbase
cd express-cloudbase

使用 Express Generator 创建应用

# 使用 Express Generator 创建应用
npx express-generator --view=pug express-app

# 进入项目目录
cd express-app

# 安装依赖
npm install

这将创建一个使用 Pug 作为视图引擎的 Express 应用程序。

本地测试应用

启动开发服务器:

npm start

打开浏览器访问 http://localhost:3000,您应该能看到 Express 欢迎页面。

第二步:添加 API 路由

让我们创建一个 RESTful API 来演示 Express 的功能。

创建用户路由

routes 目录下创建 users.js 文件:

const express = require("express");
const router = express.Router();

// 模拟用户数据
const users = [
{ id: 1, name: "zhangsan", email: "zhangsan@example.com" },
{ id: 2, name: "lisi", email: "lisi@example.com" },
{ id: 3, name: "wangwu", email: "wangwu@example.com" },
];

/* GET users listing */
router.get("/", function (req, res, next) {
const { page = 1, limit = 10 } = req.query;
const startIndex = (page - 1) * limit;
const endIndex = startIndex + parseInt(limit);

const paginatedUsers = users.slice(startIndex, endIndex);

res.json({
success: true,
data: {
total: users.length,
page: parseInt(page),
limit: parseInt(limit),
items: paginatedUsers,
},
});
});

/* GET user by ID */
router.get("/:id", function (req, res, next) {
const userId = parseInt(req.params.id);
const user = users.find((u) => u.id === userId);

if (!user) {
return res.status(404).json({
success: false,
message: "User not found",
});
}

res.json({
success: true,
data: user,
});
});

/* POST create user */
router.post("/", function (req, res, next) {
const { name, email } = req.body;

if (!name || !email) {
return res.status(400).json({
success: false,
message: "Name and email are required",
});
}

const newUser = {
id: users.length + 1,
name,
email,
};

users.push(newUser);

res.status(201).json({
success: true,
data: newUser,
});
});

module.exports = router;

创建健康检查路由

routes 目录下创建 health.js 文件:

const express = require("express");
const router = express.Router();

/* GET health check */
router.get("/", function (req, res, next) {
res.json({
status: "healthy",
timestamp: new Date().toISOString(),
framework: "Express",
version: process.env.npm_package_version || "1.0.0",
node_version: process.version,
});
});

module.exports = router;

更新应用配置

编辑 app.js 文件,添加新的路由和中间件:

var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");

var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
var healthRouter = require("./routes/health");

var app = express();

// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "pug");

app.use(logger("combined"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));

// 路由配置
app.use("/", indexRouter);
app.use("/api/users", usersRouter);
app.use("/health", healthRouter);

// 404 错误处理
app.use(function (req, res, next) {
next(createError(404));
});

// 全局错误处理
app.use(function (err, req, res, next) {
// 设置错误信息,只在开发环境提供详细错误
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};

// 返回 JSON 格式的错误信息
if (req.path.startsWith("/api/") || req.path.startsWith("/health")) {
res.status(err.status || 500).json({
success: false,
message: err.message,
error: req.app.get("env") === "development" ? err.stack : undefined,
});
} else {
// 渲染错误页面
res.status(err.status || 500);
res.render("error");
}
});

module.exports = app;

修改启动配置

编辑 bin/www 文件,确保应用监听正确的端口:

#!/usr/bin/env node

/**
* Module dependencies.
*/

var app = require("../app");
var debug = require("debug")("express-app:server");
var http = require("http");

/**
* Get port from environment and store in Express.
*/

var port = normalizePort(process.env.PORT || "9000");
app.set("port", port);

/**
* Create HTTP server.
*/

var server = http.createServer(app);

/**
* Listen on provided port, on all network interfaces.
*/

server.listen(port, "0.0.0.0");
server.on("error", onError);
server.on("listening", onListening);

/**
* Normalize a port into a number, string, or false.
*/

function normalizePort(val) {
var port = parseInt(val, 10);

if (isNaN(port)) {
// named pipe
return val;
}

if (port >= 0) {
// port number
return port;
}

return false;
}

/**
* Event listener for HTTP server "error" event.
*/

function onError(error) {
if (error.syscall !== "listen") {
throw error;
}

var bind = typeof port === "string" ? "Pipe " + port : "Port " + port;

// handle specific listen errors with friendly messages
switch (error.code) {
case "EACCES":
console.error(bind + " requires elevated privileges");
process.exit(1);
break;
case "EADDRINUSE":
console.error(bind + " is already in use");
process.exit(1);
break;
default:
throw error;
}
}

/**
* Event listener for HTTP server "listening" event.
*/

function onListening() {
var addr = server.address();
var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port;
debug("Listening on " + bind);
console.log("Express server listening on " + bind);
}

第三步:本地测试

⚠️ 重要提示:CloudBase HTTP 云函数要求应用监听 9000 端口。

启动应用:

npm start

测试 API 接口:

# 测试健康检查
curl http://localhost:9000/health

# 测试用户列表
curl http://localhost:9000/api/users

# 测试分页
curl "http://localhost:9000/api/users?page=1&limit=2"

# 测试获取单个用户
curl http://localhost:9000/api/users/1

# 测试创建用户
curl -X POST http://localhost:9000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"新用户","email":"newuser@example.com"}'

第四步:创建启动脚本

💡 注意

  • 在 windows 下创建 scf_bootstrap 文件时,优先使用 nano scf_bootstrap 或者 vim scf_bootstrap 创建
  • 在 windows 下使用 vscode 创建 scf_bootstrap 文件时,部署到 HTTP 云函数可能会报错: scf_bootstrap 文件不存在
  • 这个错误是因为脚本文件包含了 Windows 格式的回车符(^M),导致 Linux 无法正确识别解释器路径。这是 WSL 中常见的问题

创建 scf_bootstrap 文件(无扩展名):

#!/bin/bash
export PORT=9000
npm start

为启动脚本添加执行权限:

chmod +x scf_bootstrap

💡 说明

  • scf_bootstrap 是 CloudBase 云函数的启动脚本
  • 设置 PORT=9000 环境变量确保应用监听正确端口
  • 使用 npm start 启动应用

第五步:准备部署文件

确保您的项目目录结构如下:

express-app/
├── bin/
│ └── www # 启动文件
├── public/ # 静态资源
├── routes/ # 路由文件
│ ├── index.js
│ ├── users.js
│ └── health.js
├── views/ # 视图模板
├── app.js # 应用主文件
├── package.json # 项目配置
├── package-lock.json # 依赖锁定文件
└── scf_bootstrap # 启动脚本

第六步:部署到 CloudBase HTTP 云函数

通过控制台部署

  1. 登录 CloudBase 控制台
  2. 选择您的环境,进入「云函数」页面
  3. 点击「新建云函数」
  4. 选择「HTTP 云函数」
  5. 填写函数名称(如:express-app
  6. 选择运行时:Node.js 18.x(或其他支持的版本)
  7. 提交方法选择:本地上传文件夹
  8. 函数代码选择 express-app 目录进行上传
  9. 自动安装依赖:开启此选项
  10. 点击「创建」按钮等待部署完成

通过 CLI 部署

详情请参考 部署HTTP云函数

打包部署

如果需要手动打包:

# 创建部署包(排除开发文件)
zip -r express-app.zip . -x "node_modules/*" ".git/*" "*.log"

第七步:访问您的应用

部署成功后,您可以参考通过 HTTP 访问云函数设置自定义域名访问HTTP 云函数

您可以测试以下接口:

  • 根路径:/ - Express 欢迎页面
  • 健康检查:/health - 查看应用状态
  • 用户列表:/api/users - 获取用户列表
  • 用户详情:/api/users/1 - 获取特定用户
  • 创建用户:POST /api/users - 创建新用户

常见问题

Q: 为什么必须使用 9000 端口?

A: CloudBase HTTP 云函数要求应用监听 9000 端口,这是平台的标准配置。

Q: 如何处理静态文件?

A: Express 的静态文件中间件会自动处理 public 目录下的静态资源。

Q: 如何查看应用日志?

A: 在 CloudBase 控制台的云函数页面,点击函数名称进入详情页查看运行日志。

Q: 支持哪些 Node.js 版本?

A: CloudBase 支持 Node.js 16.x、18.x、20.x 等版本,建议使用最新的 LTS 版本。

Q: 如何处理 CORS 跨域问题?

A: 可以使用 cors 中间件或手动设置响应头来处理跨域请求。

最佳实践

1. 环境变量管理

app.js 中添加环境变量支持:

// 加载环境变量
require("dotenv").config();

// 使用环境变量
const isDevelopment = process.env.NODE_ENV === "development";
const port = process.env.PORT || 9000;

2. 添加 CORS 支持

安装并配置 CORS 中间件:

npm install cors
const cors = require("cors");

// 配置 CORS
app.use(
cors({
origin: process.env.ALLOWED_ORIGINS?.split(",") || "*",
credentials: true,
}),
);

3. 请求日志

使用 Morgan 中间件记录请求日志:

const morgan = require("morgan");

// 配置日志格式
app.use(morgan(process.env.NODE_ENV === "production" ? "combined" : "dev"));

4. 错误处理

实现全局错误处理中间件:

// 全局错误处理
app.use((err, req, res, next) => {
console.error(err.stack);

res.status(err.status || 500).json({
success: false,
message: err.message,
...(process.env.NODE_ENV === "development" && { stack: err.stack }),
});
});

5. 安全配置

安装并配置 Helmet 中间件:

npm install helmet
const helmet = require("helmet");

// 安全头配置
app.use(helmet());

进阶功能

数据库集成

集成 MongoDB 或 MySQL:

npm install mongoose
# 或
npm install mysql2

身份验证

添加 JWT 身份验证:

npm install jsonwebtoken bcryptjs

API 文档

使用 Swagger 生成 API 文档:

npm install swagger-jsdoc swagger-ui-express

下一步