如何使用Node.js将HTML页面渲染成HTTP服务器响应

4,000 阅读8分钟

使用Node.js将HTML页面渲染为HTTP服务器响应

在开发Web应用程序时,你可能需要在服务器内渲染HTML组件。一旦有人提出访问这些页面的请求,这将有助于在客户端创建交互式页面。

例如,有各种方式来托管你的HTML页面(网站)。

  • 使用React等框架自行渲染你的客户端,或者。
  • 直接从服务器上渲染页面。当浏览器访问你的服务器中指定的路线时,服务器将根据用户的请求加载这些HTML页面。

本指南解释了如何使用Node.js在服务器上渲染HTML元素和HTML页面。

前提条件

使用[Node.js]和[Express.js]的基本知识将有助于学习。

设置

  • [下载Node.js]并安装它。运行node -v ,以测试安装是否成功。
$ node -v
v12.18.3 ## installed Node.js version
  • 一旦Node.js被成功安装,NPM也将被安装。运行npm -v ,确认是否真的安装了NPM。
$ npm -v
7.6.3 ## installed npm version
  • 创建一个Node.js项目目录,并在此目录下初始化项目。使用npm init -y 来自动初始化这个Node.js项目。
  • 使用npm install express 安装Express.js框架。
  • 我们将使用Express.js创建一个服务器。用Nodemon钩住服务器是很重要的。Nodemon是一个可选的软件包(全局安装),它在保存服务器端的代码修改后自动重启服务器。继续使用npm install -g nodemon 来安装Nodemon。

将内联HTML元素渲染成HTTP响应

下面是一个简单的hello world HTTP服务器(app.js) ,监听端口为3000。

文件名app.js

const express = require("express");
const app = express();

app.listen(3000, () => {
  console.log("Application started and Listening on port 3000");
});

app.get("/", (req, res) => {
  res.send("Hello world!");
});

运行nodemon 来启动服务器。

每当服务器运行,并访问路由http://localhost:3000/ ,它将输出纯文本hello world!

我们可以使用同一个服务器来渲染HTML元素作为服务器响应,而不是发送纯文本。

下面是一些HTML元素的列表。我们可以通过指定默认路由被访问时要发送的响应,将它们直接渲染到我们的服务器中。

const express = require("express");
const app = express();

app.listen(3000, () => {
  console.log("Application started and Listening on port 3000");
});

app.get("/", (req, res) => {
  res.send("<html> <head>server Response</head><body><h1> This page was render direcly from the server <p>Hello there welcome to my website</p></h1></body></html>");
});

重新启动服务器,在浏览器上打开路由http://localhost:3000/

Server rendered html elements

res.send 是向服务器发送单个的HTML数据位,但如果我们想发送整个网页,如 ,我们必须使用不同的东西。index.html

将HTML网页渲染成服务器响应

上面的方法可能非常令人厌烦,而且可能不是你想在服务器内部编写的代码类型。在一个正常的网页中,HTML元素被写在一个.html 文件中。

这使得你更容易编写所有的HTML元素,包括CSS样式,来布局这些元素。这样就把服务器文件和HTML元素分开了,创造了一个干净的代码设置。

为了使用Express.js将HTML文件渲染到服务器上,我们使用res.sendFile() 。这可以读取并渲染一个人的HTML文件中包含的数据。

这在向服务器发出GET请求时将文件传输到浏览器。服务器提交一个响应状态,以HTML渲染的网页内容作为信息主体。

下面是res.sendFile() 的语法。

res.sendFile(path [, options] [, fn])

路径指定了HTML文件的位置。它需要一个文件名的数组,如:index.html 。在某些情况下,主要是当服务器托管在云端时(当服务器没有托管在你的电脑本地时),我们使用__dirname 而不是相对文件路径。

当服务器在线时,你可能不知道你的HTML文件的位置。__dirname 将返回当前的文件路径,无论它在你的项目文件夹内被托管在哪里。

让我们用一个简单的例子来证明__dirname 是如何工作的,console.log(__dirname)

const express = require("express");
const app = express();

app.listen(3000, () => {
  console.log("Application started and Listening on port 3000");
});

app.get("/", (req, res) => {
  console.log(__dirname)
});

在浏览器上打开路由http://localhost:3000/ 。这将把__dirname 的值打印到控制台。

How dirname works

正如你所看到的,它打印的是到达你的服务器位置的确切路径。

现在我们可以使用__dirname 将HTML文件发送到服务器上。

首先,创建一个HTML表单(index.html),并包括一些CSS样式(app.css),如下所示。

文件名index.html

<!DOCTYPE html>
<html lang="en"> 
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="app.css">
  <link rel="stylesheet"
  href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.no-icons.min.css">
  <title>html form</title>
</head>
<body>
<div class="subscribe-container">
  <form method="POST">
    <input type="email" name="youremail" placeholder="Email Address" required>
    <input type="text" name="yourname" placeholder="Name" required>
    <input class="subscribe-button" type="submit" value="Subscribe">
  </form>
</div>
</body>
</html>

文件名app.css

.subscribe-container {
    max-width: 800px;
    margin: 60px auto;
    background-color: rgba(130, 184, 219, 0.5);
    border: 5px solid rgb(98, 143, 228);
}
.subscribe-container form {
    display: flex;
    flex-wrap: wrap;
}
.subscribe-container form input {
    margin: 15px;
    border: 1px solid rgb(98, 143, 228);
    padding: 0.4rem;
}
.subscribe-container form input {
    flex: 1 1 200px;
}

.subscribe-container form input[type="submit"] {
    border-radius: 3px;
    background-color: rgba(17, 228, 10, 0.5);
color: rgb(98, 143, 228);
}

.subscribe-container form input[type="email"] {
    flex: 1 1 250px;
}

让我们把文件渲染到服务器上。

const express = require("express");
const app = express();

app.listen(3000, () => {
  console.log("Application started and Listening on port 3000");
});

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

Html page as server response

然而,这并没有加载CSS样式。app.css 是一个静态文件。要加载包含在index.html 中的静态服务器文件,请使用express.static ,如下面的例子所示。

const express = require("express");
const app = express();

app.listen(3000, () => {
  console.log("Application started and Listening on port 3000");
});

// serve your css as static
app.use(express.static(__dirname));

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

保存该文件并在浏览器中打开http://localhost:3000/ ,服务器将按预期发送一个网页。

Html page with css as server response

使用HTML表单向服务器解析表单数据

服务器正在运行。它现在正在向客户(浏览器)返回HTML表单作为响应。每当这个服务器的路由被访问时,GET 请求将从浏览器中被执行。然而,如果你把数据填入这个表单并按下订阅按钮,会发生什么?

让我们来试试。这样就会输出下面的结果,即服务器返回的错误。

Server POST request error

再次重新加载该页面。打开浏览器检查器工具,进入网络标签。填写表格数据并点击订阅按钮。这将返回一个404状态代码。这意味着客户端不能向服务器发送/POST 数据。

Browser inspector tool network status code

我们创建的HTML表单有一个POST 方法。这意味着我们正在向服务器发送一个POST 请求。

我们的服务器没有办法处理来自客户端的任何POST 请求。服务器没有给客户端POST 这个路径的权限。

我们可以通过给路由添加一个POST 方法来解决这个问题。这将处理来自此路由的任何POST 请求。

const express = require("express");
const app = express();

app.listen(3000, () => {
  console.log("Application started and Listening on port 3000");
});

// server your css as static
app.use(express.static(__dirname));

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

app.post("/", (req, res) => {
  res.send("Thank you for subscribing");
});

当你点击订阅按钮时,一条Thank you for subscribing 的信息将被打印在浏览器上。此外,浏览器在检查检查器网络时将返回一个200代码,这是好的。客户端有POST 的权限,可以向服务器发送一个POST 的请求。

Browser inspector tool network error resolved

一切都运行得很好。然而,我们需要服务器获取表单数据,并将相关结果发送给浏览器,而不是发送一些相对的纯文本,如Thank you for subscribing

为了与表单数据交互,我们需要一个body-parser包。继续使用npm install body-parser 来安装这个包。Body-parser有助于在你的处理程序之前在一个中间件中解析传入的请求体,在req.body属性下可用。

使用require() 函数导入该包,并通过app.use 让服务器使用它。

Body-parser有几种模式,比如。

  • bodyParser.text - 把所有的请求传成文本。
  • bodyParser.json - 把数据解析成JSON格式。
  • bodyParser.urlencoded - 通常在获取从HTML表单中发布的数据时使用。

在这个例子中,我们将使用bodyParser.urlencoded 格式来与表单数据进行交互。

Body-parser进入你的任何使用req.body 的路由,并获得向服务器发出的HTTP请求的解析版本。

通过body-parser,我们可以访问这些表单数据并与之交互。让我们尝试用控制台日志req.body ,以获得一个解析过的HTTP请求的抓取。

const express = require("express");
const bodyParser = require("body-parser");
const app = express();

app.listen(3000, () => {
  console.log("Application started and Listening on port 3000");
});

// server css as static
app.use(express.static(__dirname));

// get our app to use body parser 
app.use(bodyParser.urlencoded({ extended: true }))

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

app.post("/", (req, res) => {
  console.log(req.body)
});

打开http://localhost:3000/ 。填写表单输入并点击订阅按钮。检查你的控制台。

Console log a request body

这个表单数据是现成的,我们现在可以指示服务器如何处理它。

const express = require("express");
const bodyParser = require("body-parser");
const app = express();

app.listen(3000, () => {
  console.log("Application started and Listening on port 3000");
});

// server css as static
app.use(express.static(__dirname));

// get our app to use body parser 
app.use(bodyParser.urlencoded({ extended: true }))

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

app.post("/", (req, res) => {
  var subName = req.body.yourname
  var subEmail = req.body.youremail;
 res.send("Hello " + subName + ", Thank you for subcribing. You email is " + subEmail);
});

填写表格输入并点击订阅按钮。

Using body-parser

注意:存储在变量subNamesubEmail 中的信息分别对应于yournameyouremailyournameyourname 命名来自于你的HTML表单输入的name attribute 。这样,你可以使用表单数据并决定每个输入的情况,就像它们只是对象主体的属性一样。

总结

我希望本指南能帮助你了解如何使用Express.js将HTML数据渲染到你的服务器中。

[pug]和[ejs]等模板引擎也可以用来向服务器渲染动态HTML数据。它们都是使用Express.js这样的后备技术来编译HTML。