基于Express和Handlebars搭建Web服务
0.概述
这里先构建一个基础的web骨架,然后再设计后端业务逻辑,数据结构,以及数据交互,页面展示
由于express比较经典,资料相对也多,当然也可以用别的,比如Koa,NestJS,Fastify,Hapi.js;模板引擎使用Handlebars,主要是逻辑简单;其他的 EJS (Embedded JavaScript),Pug (原名 Jade)等也是可以的。
1.框架搭建
可以采用Express应用生成器,或者手动搭建
1.1全局安装生成器
css
npm i -g express-generator
1.2.生成框架
ini
express --view=hbs NodeIOT
1.3.安装依赖,运行
css
npm i
npm start
打开浏览器访问,默认端口3000
1.4.效果如图

2. Express
由于平台要设置权限,后面会增加登录接口,这里要启用cookie以及校验,页面的请求使用json格式;Express的核心语法围绕路由、中间件、请求 / 响应对象 和 应用配置这几个部分
2.1.路由定义了应用如何响应客户端的请求,是 Express 的核心逻辑
javascript
app.get('/', (req, res) => {
res.send('Hello, Express!'); // 发送文本响应
});
app.post('/login', (req, res) => {
const { username, password } = req.body;
if (username === 'admin' && password === 'secret') {
res.status(200).json({ success: true, role: 'admin' });
} else {
res.status(401).send('Unauthorized');
}
});
2.2.中间件是处理请求 / 响应流程的函数,用于增强请求处理流程(如解析、验证),可用于:
- 解析请求体(如 JSON、表单数据)
- 记录日志
- 验证用户权限
- 处理静态文件
javascript
//自定义中间件
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
});
2.3.请求(Request)与响应(Response)对象,封装了客户端请求数据和服务端响应方法
请求对象 req
常用属性和方法:
- req.method:请求方法(GET/POST 等)
- req.url:请求路径
- req.params:动态路由参数(如 /user/:id 中的 id)
- req.query:查询参数(如 ?name=alice)
- req.body:解析后的请求体数据(需配合中间件)
响应对象 res
常用方法:
- res.send([BODY]):发送任意类型响应(自动设置 Content-Type)
- res.json([OBJECT]):发送 JSON 响应(设置 Content-Type: application/json)
- res.status([CODE]).send([MESSAGE]):设置状态码并发送响应
- res.redirect([URL]):重定向请求
2.4.应用配置(App Settings),控制应用行为(如视图引擎、环境变量)
javascript
// 设置视图引擎(如 EJS/Pug)
app.set('view engine', 'ejs');
app.set('views', './views'); // 设置视图文件存放目录
// 获取环境变量(开发/生产模式)
app.set('env', process.env.NODE_ENV || 'development');
const isProduction = app.get('env') === 'production';
3. Handlebars
Handlebars 是一个语义化的模板引擎,通过简单的标记语法将数据与视图分离。以下是其核心语法的详细说明:
3.1 基础渲染
- 使用双花括号 {{variable}} 输出变量值
- 使用三花括号 {{{html}}} 输出未转义的 HTML
3.2 控制结构
-
- 条件判断(if/else)
-
- 循环(each)
-
- 当前对象(this)
3.3 表达式与帮助方法
- {{length array}}:获取数组长度
- {{if condition}}:条件判断
- {{each array}}:循环
- {{log message}}:调试日志
3.4 片段模板(Partials)
handlebars
<!-- 定义部分模板(header.hbs) -->
<header>
<h1>{{title}}</h1>
</header>
<!-- 在主模板中使用 -->
{{> header title="My Website"}}
3.5 模板继承(Layouts)
3.6 数据处理
路径访问
handlebars
<!-- 模板 -->
<div>{{user.profile.name}}</div>
<!-- 数据 -->
{
"user": {
"profile": { "name": "Alice" }
}
}
<!-- 渲染结果 -->
<div>Alice</div>
参数传递
handlebars
{{link "Home" url="/"}} <!-- 传递参数给自定义帮助方法 -->
3.7 简单示例
handlebars
<!-- 模板(index.hbs) -->
<!DOCTYPE html>
<html>
<head>
<title>{{siteTitle}}</title>
</head>
<body>
<h1>{{welcomeMessage}}</h1>
{{#if showUsers}}
<ul>
{{#each users}}
<li>{{name}} ({{age}})</li>
{{/each}}
</ul>
{{else}}
<p>无用户信息可查.</p>
{{/if}}
<button onclick="{{action}}">点击</button>
</body>
</html>
<!-- 数据 -->
app.get('/', (req, res) => {
res.render('home',
{
post: [
{
"siteTitle": "My Website",
"welcomeMessage": "Welcome to our site!",
"showUsers": true,
"users": [
{ "name": "Alice", "age": 30 },
{ "name": "Bob", "age": 25 }
],
"action": "submitForm()"
}
]
});
});
4 实现
启动主程序,用于接收设备的协议数据,然后将数据给子进程,子进程启动web监听,通过websocket交给页面,
4.1 创建全局变量
angular2html
var publicVar = {
exphbs: null,
webPort: 8000,
};
4.2 初始化变量
php
publicVar.exphbs = exphbs.create({
layoutsDir: path.resolve(__dirname, "../views/layouts/"),
partialsDir: path.resolve(__dirname, "../views/partials/"),
defaultLayout: "main",
extname: ".hbs"
})
4.3 主程序
javascript
function run(){
if(cluster.isMaster) {
workTask = function () {
var task = cluster.fork();
task.on('message', function (msg) {
console.log('接收子进程的消息', msg);
})
}
require("os").cpus().forEach((function () {
workTask()
}
} else {
require("./webProcess.js").start()
}
}
4.4 子程序
javascript
function start() {
//1.配置模板
app.engine("hbs", publicVar.exphbs.engine),
app.set("views", path.resolve(__dirname, "../views/")),
app.set("view engine", "hbs"),
app.use(bodyParser.json({
limit: "50mb"
})),
app.use(bodyParser.urlencoded({
limit: "50mb"
}))
//2.启用Cookie解析功能
app.use(cookieParser(publicVar.cookieSecret))
//3.web监听
server.listen(publicVar.webPort, (function () {
console.log("Server has started. port: " + publicVar.webPort)
}));
//4.处理websocket请求
io.sockets.on("connect", (function (sock) {
console.log("接收到新的websocket客户端连接")
}));
//5.接收主程序的数据
process.on("message", (function (msg) {
console.log("接收到master的消息")
}
}