Express
Express æ¯ä¸ä¸ªè½»é级ãçµæ´»ç Node.js Web æ¡æ¶ï¼ä»¥å ¶ç®åæç¨åé«åº¦å¯æ©å±èç§°ã宿ä¾äºç®æ´ç API è®¾è®¡ï¼æ¯æä¸é´ä»¶æºå¶ï¼è½å¿«éæå»º RESTful API æå ¨æ åºç¨ãExpress æ¥æä¸°å¯çæä»¶çæï¼å¯è½»æ¾éææ°æ®åºã身份éªè¯çåè½ï¼åæ¶ä¿æé«æ§è½åä½å¦ä¹ ææ¬ï¼æ¯ Node.js å¼åè çé¦éæ¡æ¶ä¹ä¸ã
æ¬æåä»ç»å¦ä½å¨ CloudBase HTTP äºå½æ°ä¸é¨ç½² Express åºç¨ç¨åºã
åç½®æ¡ä»¶â
å¨å¼å§ä¹åï¼è¯·ç¡®ä¿æ¨å·²ç»ï¼
- å®è£ äº Node.js 18.x ææ´é«çæ¬
- æ¥æè ¾è®¯äºè´¦å·å¹¶å建äºäºå¼åç¯å¢
- äºè§£åºæ¬ç 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 äºå½æ°â
éè¿æ§å¶å°é¨ç½²â
- ç»å½ CloudBase æ§å¶å°
- éæ©æ¨çç¯å¢ï¼è¿å ¥ãäºå½æ°ã页é¢
- ç¹å»ãæ°å»ºäºå½æ°ã
- éæ©ãHTTP äºå½æ°ã
- å¡«å彿°åç§°ï¼å¦ï¼
express-appï¼ - éæ©è¿è¡æ¶ï¼Node.js 18.xï¼æå ¶ä»æ¯æççæ¬ï¼
- æäº¤æ¹æ³éæ©ï¼æ¬å°ä¸ä¼ æä»¶å¤¹
- 彿°ä»£ç éæ©
express-appç®å½è¿è¡ä¸ä¼ - èªå¨å®è£ ä¾èµï¼å¼å¯æ¤é项
- ç¹å»ãå建ãæé®çå¾ é¨ç½²å®æ
éè¿ 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 端å£ï¼è¿æ¯å¹³å°çæ åé ç½®ã