基于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 展望

下一步在这个的基础上添加后台业务逻辑,数据存储和页面渲染。
相关推荐
优创学社219 分钟前
基于springboot的社区生鲜团购系统
java·spring boot·后端
why技术23 分钟前
Stack Overflow,轰然倒下!
前端·人工智能·后端
幽络源小助理26 分钟前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
ai小鬼头2 小时前
AIStarter如何助力用户与创作者?Stable Diffusion一键管理教程!
后端·架构·github
简佐义的博客2 小时前
破解非模式物种GO/KEGG注释难题
开发语言·数据库·后端·oracle·golang
Code blocks2 小时前
使用Jenkins完成springboot项目快速更新
java·运维·spring boot·后端·jenkins
追逐时光者3 小时前
一款开源免费、通用的 WPF 主题控件包
后端·.net
蜗牛沐雨3 小时前
警惕 Rust 字符串的性能陷阱:`chars().nth()` 的深坑与高效之道
开发语言·后端·rust