基于Express和Handlebars搭建Web服务

基于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 控制结构
    1. 条件判断(if/else)
    1. 循环(each)
    1. 当前对象(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的消息")
    }
}

5 展望

下一步在这个的基础上添加后台业务逻辑,数据存储和页面渲染。
相关推荐
皮皮高38 分钟前
itvbox绿豆影视tvbox手机版影视APP源码分享搭建教程
android·前端·后端·开源·tv
弱冠少年42 分钟前
golang入门
开发语言·后端·golang
Humbunklung1 小时前
Rust 函数
开发语言·后端·rust
喜欢踢足球的老罗1 小时前
在Spring Boot 3.3中使用Druid数据源及其监控功能
java·spring boot·后端·druid
jakeswang1 小时前
StarRocks
后端·架构
龙云飞谷1 小时前
从原理到调参,小白也能读懂的大模型微调算法Lora
后端
荣江1 小时前
【实战】基于 Tauri 和 Rust 实现基于无头浏览器的高可用网页抓取
后端·rust
寻月隐君1 小时前
Web3实战:Solana CPI全解析,从Anchor封装到PDA转账
后端·web3·github
程序员小假1 小时前
说一说 SpringBoot 中 CommandLineRunner
java·后端
sky_ph2 小时前
JAVA-GC浅析(一)
java·后端