使用Node.js构建收费公路管理系统
在本文中,我们将使用Node.js构建一个简单的收费公路管理系统,其中数据将存储在本地MongoDB数据库中。
问题陈述: 在一个收费站,很难记录所有的交易并将它们存储在一个地方,如果需要的话,很难列出通过该收费站的车辆的数据。我们的目标是构建一个应用程序,允许收费管理员将收费收据添加到我们的数据库中,并且所有收据(通过收费站的车辆的详细信息)都应该对收费管理员可见。
因此,我们将使用以下方法构建我们的应用程序: 我们将使用简单的HTML(以及一些CSS)和Bootstrap构建前端,后端将使用Node.js,并且我们将使用本地MongoDB数据库。
我们的应用程序基本上包含以下两个页面,我们将在文章中进一步构建它们:
- 收费员可以从中添加通过收费的车辆的详细信息(车牌号码,日期,时间等)的简单页面。
- 我们将列出所有的交易或收费收据的详细信息的页面。
以下是我们的应用程序的外观示例:

让我们开始构建我们的应用程序。
在我们的应用程序中安装所需的软件包: 首先,您必须对Node软件包(如“express”,“body-parser”,“ejs”和“mongoose”)有基本了解,并且在您的电脑上安装了MongoDB。如果您对这些软件包之一没有基本了解或尚未安装MongoDB,建议您参考提供的文章链接。当我们逐步了解代码时,将提供对所有软件包的简要描述。
所以,首先,导航到一个新的文件夹/目录,并通过在命令行中执行以下命令来在其中创建一个Server.js文件:

现在我们已经创建了server.js文件,我们需要使用以下命令在我们的文件夹中初始化npm:

一旦npm被初始化,我们就可以安装所需的包,包括express、ejs、body-parser和mongoose。执行以下命令来一次性安装所有这些包。

到目前为止,项目的结构应该是这样的:

现在,在Server.js文件中,我们将要求安装在上面的所有包,以便我们稍后可以在Server.js文件中使用它们。在server.js文件中,我们必须添加以下代码:
Server.js文件:
// Server.js
// Adding require statement for express
const express = require("express");
// Making app variable using express
const app = express();
// Adding require statement for mongoose package
const mongoose = require("mongoose");
// Adding require statement for ejs
const ejs = require("ejs");
// Setting view engine to ejs
app.set("view engine", "ejs");
// Adding require statement for body-parser
const bodyParser = require("body-parser");
使用express处理/newReceipt路由: 现在,我们将使用express来处理应用程序的/newReceipt路由(这将允许收费管理员填写有关通过收费站的车辆的详细信息)。首先,我们将允许我们的应用程序在特定端口上侦听,然后我们必须创建不同的路由来处理不同的请求。这是我们将如何做到:
Server.js文件:
// Server.js continued
// Handling get request on our home route.
app.get("/newReceipt", function (req, res) {
res.send("got request on newReceipt route");
});
// Allowing our app to listen on port 8080
app.listen(8080, function () {
console.log("Server listening on port 8080");
});
将应用程序连接到数据库:
现在我们需要使用上面安装的“mongoose”包来设置我们的Mongo数据库。首先,我们需要使用下面给出的代码将mongoose连接到我们的本地数据库:
Server.js:
// Server.js continued
// Line below will connect our app to the local
// database with the name "transactionDatabase"
// which will store all of our toll transactions
mongoose.connect("mongodb://127.0.0.1/transactionDatabase", {
useNewUrlParser: true,
useUnifiedTopology: true,
});
在这里,数据库的名称被设定为 transactionDatabase (因为它将存储所有的交易)。上述代码将创建一个名为”transactionDatabase”的新数据库,并将其与Mongoose连接。
现在,我们需要为我们的交易创建模式,即以什么格式和哪些详细信息存储我们的过路费交易。因此,我们的记录中将有4个要素,即车辆号码、缴纳的过路费金额、记录的日期和时间。以下是我们将使用的模式的创建方式:
Server.js:
// Server.js continued
// Transaction schema is the schema for our
// records that we are going to store
// It contains vehicleNumber in the form of
// string, date as string, time as string
// and the toll amount as number
const transactionSchema = new mongoose.Schema({
vehicleNumber: String,
date: String,
time: String,
tollAmount: Number,
});
在变量transactionSchema里,我们有vehicleNumber、date和time这些是字符串类型; tollAmount是数字类型。所以,这就是我们要在数据库中存储的交易的模式。 既然我们已经创建了模式,我们还要为它创建一个模型,这样我们就可以创建该模式的实例了。 下面的代码将允许我们使用我们的模式创建模型,我们以后可以使用它来创建实例。
Server.js:
// Creating the schema below
const Transaction = mongoose.model(
"Transaction", transactionSchema);
我们的模型将被命名为”Transaction”,它使用的模式是transactionSchema,我们在上面创建过。 现在我们准备使用我们的模型创建一个新的Transaction(其中包含车辆号码,过路费金额,日期和时间的详细信息),但是为此,我们需要与通过收费站的车辆相关的数据,这些数据将由收费员记录。为此,我们必须创建一个HTML页面,允许我们输入与车辆号码,过路费金额等相关的数据。 创建一个用于输入过路费交易信息的HTML文件: 我们在这里使用了Bootstrap,以使事情更加整洁和清晰,因此请确保在HTML文件中添加Bootstrap CDN。 创建一个名为newReceipt.html的文件,并将以下代码添加到其中。
NewReceipt.html:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link href=
"https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity=
"sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossorigin="anonymous">
<title>New Record</title>
</head>
<body style="padding:1%;">
<div class="">
<h1>New Transaction</h1>
</div>
<form class="" action="/newReceipt" method="post">
<label for="">Vehicle Number</label>
<input type="text" name="vehicleNumber"
value="" style="margin-bottom:1%;">
<br>
<label for="">Amount</label>
<input type="number" name="amount"
value="" style="margin-bottom:1%;">
<br>
<button type="submit" name="button">
Add Transaction
</button>
</form>
</body>
</html>
以下是上述代码的解释:
- 首先,在head标签中添加了bootstrap CDN,以使网页看起来更整洁。
- 在body中,我们有一个包含标题和表单元素的div,用于收集来自收费员的数据。
- 在表单元素内部,我们有两个输入框,一个是车辆编号,其name属性为”vehicleNumber”,另一个属性是收费金额,其name属性为”amount”。
- 然后我们有一个按钮,类型为submit,即当点击时提交所有表单数据。
- 表单元素具有一个action属性,它的值设置为”/newReceipt”,也就是说,当表单被提交时,我们将到达”/newReceipt”路由,并且method属性指定了我们将要发送的请求类型,这里是”post”请求。
现在我们已经创建了一个名为newReceipt.html的简单HTML页面,我们必须确保当app.get(“/newReceipt”)被调用时,我们可以将此页面作为响应提供。为了更好地理解,请参考下面的代码: 在home(“/”)路由上提供NewReceipt.html文件:
Server.js:
// Server.js
// Handling the new receipt route
// passing the newReceipt.html file on this route
app.get("/newReceipt", function (req, res) {
res.sendFile(__dirname + "/newReceipt.html");
});
这段代码需要添加到Server.js文件中,当我们进入/newReceipt路由时,它将作为响应提供给我们创建的newReceipt.html页面。
因此,让我们通过启动服务器并转到/newReceipt路由来检查一切是否正常工作。首先使用下面显示的命令启动服务器:

到目前为止,如果一切正常,我们可以进一步创建一个新的交易并将其存储在transactionDatabase中。为此,我们将从在“newReceipt.html”中创建的表单元素中获取数据。
从在“NewReceipt.html”文件中创建的表单中获取数据并将数据添加到我们的数据库中: 为了读取表单数据,我们将使用一个称为“body-parser”的包,该包已在上面安装。为了使用它,我们首先要告诉我们的应用程序使用bodyParser,这可以通过在Server.js文件的require package行下面添加一行来完成:
Server.js:
// Server.js
const bodyParser = require("body-parser");
// Allowing our app to use bodyParser
app.use(
bodyParser.urlencoded({
extended: true,
})
);
在“newReceipt.html”中的表单元素,在提交时会生成一个POST请求到/newReceipt路由,我们需要在Server.js文件中处理,代码如下所示:
// Server.js
// Handling post request on the /newReceipt route
app.post("/newReceipt", function (req, res) {
// Getting the vehicle Number from the request,
// which was entered in the newReceipt.html
// page
let vNo = req.body.vehicleNumber;
// Getting the amount from the request.
let amt = req.body.amount;
// Creating a new transaction which we
// are going to save in database.
var t = new Transaction({
vehicleNumber: vNo,
date: getDate(),
time: getTime(),
tollAmount: amt,
});
// Saving the transaction in the database.
t.save();
// Sending response that we have saved the data
res.send("Saved the data");
});
// Function to get the current date
function getDate() {
// Creating new date object
var date = new Date();
// Converting date to string
date = date.toString();
// Returning the date
return date.substring(0, 10);
}
// Function to get the current time
function getTime() {
var today = new Date();
return today.getHours() + ":"
+ today.getMinutes()
+ ":" + today.getSeconds();
}
以下是代码的解释:
- req.body.vehicleNumber和req.body.amount用于获取newReceipt.html中的表单元素中的输入。
- 使用4个参数创建了一个新的交易:vehicleNumber(从请求中获取),date(从一个名为getDate()的函数获取),time(从一个名为getTime()的简单函数获取)和tollAmount(从请求中获取)。
- “t.save()”函数将名为t的交易保存在我们的transactionDatabase中。
- getDate()是一个简单的函数,用于使用Javascript Date()获取当前日期。
- getTime()是一个类似getDate()的简单函数,用于获取当前时间。
- res.send()函数用于通过发送响应来结束请求,指示数据已保存。
现在,一旦我们通过单击表单中的按钮提交表单数据,我们将在我们的transaction database中保存一个新的transaction实例。
让我们检查到目前为止的进展。我们将再次启动服务器,使用下面显示的命令:

一旦服务器启动,我们可以继续访问/newReceipt路由,以获取“newReceipt.html”作为响应。一旦我们输入vehicleNumber和amount,并点击按钮,我们将看到一个响应“已保存”,说明数据已保存。以下是示例演示:

现在我们已经将事务保存在数据库中,我们还需要显示这些事务。为此,我们将在主页路由中创建一个获取请求,以表格格式显示所有记录。为此,我们需要在Server.js文件中添加以下代码:
从Server.js文件向主页路由发送从数据库中获取的数据:
Server.js:
// Server.js
// Handling request on home route
app.get("/", function (req, res) {
res.send("got a request on home route");
});
为了从我们的transactionDatabase中获取数据,我们需要使用Mongoose软件包,并且我们要使用的函数是find(),它会接受两个参数,一个是条件(在这种情况下没有条件,因为我们要获取所有记录),根据该条件从数据库中获取数据,第二个是回调函数。下面的代码示例了这一点:
Server.js:
// Server.js
// Handling the home route
app.get("/", function (req, res) {
// Fetching all the transaction data from
// the database
Transaction.find({}, function (err, docs) {
// Sending the transactions found as
// a response to this route
res.send(docs);
});
});
在上面的代码中,有几点需要注意: 第一个参数在find()函数中保持空白,因为我们想要获取所有记录而不带任何条件。 回调函数中的“docs”参数包含了我们从数据库中提取的所有数据。 res.send()方法以JSON格式将所有提取的数据作为响应发送。 所以,让我们再次测试一下。重新启动服务器,使用命令“node server.js”(如下面的GIF图像所示),然后进入主页路径,你将看到一个像这样的响应:

到目前为止,我们已经从主页路由获取了transactionDatabase的数据,但数据以原始的JSON格式呈现。
以系统化的方式展示数据: 现在,我们的最后任务是以表格形式以系统化的方式展示这些数据。为此,我们将使用一个名为“ejs”的软件包,它是我们之前已经安装的软件包,它基本上允许我们在HTML中使用Server.js文件的变量。首先,我们需要在目录中创建一个“views”文件夹,然后在该文件夹中创建一个新的“index.ejs”文件。注意,我们也可以在ejs文件中编写简单的HTML代码。因此,让我们从将简单的HTML内容添加到我们的index.ejs页面开始。
index.ejs文件的起始代码如下(基本的HTML代码):
<!-- index.ejs file -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>All Transactions</title>
</head>
<body>
<h1>List of All Transactions</h1>
</body>
</html>
现在,在处理主页路由的get请求时,我们必须传递index.ejs文件,以及我们从transactionDatabase获取到的数据。为此,在处理主页路由的get请求时,我们必须对主页路由进行以下更改:
不再使用res.send()方法,而是使用res.render()方法,其中我们将传递两个参数,第一个是我们要作为响应发送的ejs文件,第二个是我们要发送到ejs文件的数据。
具体代码如下:
Server.js:
// Server.js
app.get("/", function (req, res) {
// Finding all the transactions
Transaction.find({}, function (err, docs) {
// We are not going to use res.send() function
// res.send(docs);
// Using res.render() method instead of
// res.send() method
// Using res.render() method, we can
// pass data to the front-end
res.render("index", {
docs: docs,
});
});
});
以下是与上述代码相关的注意事项:
- 在res.render()方法中,第一个参数是’index’,用于指定我们将’index.ejs’文件作为响应发送。
- res.render()方法中的第二个参数是docs,它是从transactionDatabase中获取的数据(所有收费记录),我们将docs变量发送到我们的”index.ejs”文件中,在那里可以以表格的形式显示这些数据。
现在,在index.ejs文件中,我们需要创建表格并使用ejs来正确排列从Server.js文件获取的数据。
为此,首先我们需要创建表格元素,然后使用以下代码添加表格标题:
Index.ejs:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link href=
"https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity=
"sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossorigin="anonymous">
</head>
<body style="padding:2%;">
<div class="">
<h1>List of all Transactions</h1>
</div>
<div class="transactions-table"
style="border:2px solid black;">
<table class="table">
<thead>
<tr>
<th scope="col">Sr. No.</th>
<th scope="col">Vehicle Number</th>
<th scope="col">Date</th>
<th scope="col">Time</th>
<th scope="col">Amount</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</body>
</html>
在上面给出的代码中,有几个重要的要点需要注意:
- 我们添加了Bootstrap CDN,以使表格看起来更清晰,整洁,并且还添加了一些简单的内联CSS,使事情看起来更有条理。
- 到目前为止,我们只添加了表格的标题,接下来的几个步骤中,我们将把从Server.js文件中获取到的数据添加到我们的表格正文中。
在继续下一步之前,您必须对Nodejs的ejs软件包有足够的了解。
向Index.ejs文件添加Javascript代码: 现在,我们将在HTML代码中或index.ejs文件中使用Javascript,这是ejs允许我们做的事情。
这是我们将要做的事情:
- 我们将在index.ejs文件中创建一个简单的for循环,该循环将运行与我们的docs变量中记录数一样多的次数,而docs变量则是我们从Server.js文件获取到的数据。
- 对于每条记录,我们将创建一个新的
<tr>表格行)标签,并在每个“tr”标签内部添加5个<td>(表格数据)标签。 - 这5个
<td>标签将根据记录中的数据依次包含Sr. No.、Vehicle Number、Date、Time和Amount。
这是我们将要这样做的方式(下面仅提供了表格正文标签的代码,即<tbody>):
Index.ejs:
<!-- index.ejs file -->
<tbody>
<% for(var i=0; i< docs.length; i++) { %>
<tr>
<td>
<%= i %>
</td>
<td>
<tbody>
<% for(var i=0; i< docs.length; i++) { %>
<tr>
<td>
<%= i %>
</td>
<td>
<%= docs[i].vehicleNumber %>
</td>
<td>
<%= docs[i].date %>
</td>
<td>
<%= docs[i].time %>
</td>
<td>
<%= docs[i].tollAmount %>
</td>
</tr>
<% } %>
</tbody>
<%= docs[i].vehicleNumber %>
</td>
<td>
<%= docs[i].date %>
</td>
<td>
<%= docs[i].time %>
</td>
<td>
<%= docs[i].tollAmount %>
</td>
</tr>
<% } %>
</tbody>
上面的HTML代码看起来有点奇怪,因为我们在其中使用了Javascript,用于创建for循环并使用Javascript变量。这是对上面代码的解释:
- “
<% %>”是用于在扩展名为ejs的文件中写入一行Javascript代码的括号。所以,在这里,我们在“<% %>”标签中写入了for循环。 - “docs.length”表示我们数据库中的交易记录数量,上面创建的for循环将运行这么多次。
- docs[i]表示我们数据库中的第i条记录,docs[i].vehicleNumber表示第i条记录中的vehicleNumber变量。(同样,docs[i].date代表第i条记录中的date变量,以此类推)。
- 每次运行for循环时,我们将创建一行(
<tr>标签),并在一行中使用5个<td>标签设置5个数据变量。
在这里还有最后一件事要做的是,在我们的主页或交易页面上添加一个“添加交易”按钮,点击该按钮,我们可以生成一个新的交易。我们的index.ejs文件中只需要一个简单的表单元素。代码如下所示:
Index.ejs:
<form class="" action="/newReceipt" method="get">
<button type="submit" name="button"
style="margin-top:1%;">
Add Transaction
</button>
</form>
现在我们已经完成了index.ejs文件,我们已经完成了我们的应用程序,现在我们将进行最后一次测试。因此,请使用如下所示的“node server.js”命令重新启动服务器:

我们应用的结果如下所示:

完整代码: 下面给出了Server.js文件和NewReceipt.html文件的完整代码:
// Server.js
const express = require("express");
const app = express();
const mongoose = require("mongoose");
const ejs = require("ejs");
app.set("view engine", "ejs");
const bodyParser = require("body-parser");
app.use(
bodyParser.urlencoded({
extended: true,
})
);
app.set("view engine", "ejs");
mongoose.connect(
"mongodb://127.0.0.1/transactionDatabase", {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const transactionSchema = new mongoose.Schema({
vehicleNumber: String,
date: String,
time: String,
tollAmount: Number,
});
const Transaction = mongoose.model(
"Transaction", transactionSchema);
app.get("/", function (req, res) {
Transaction.find({}, function (err, docs) {
// res.send(docs);
res.render("index", {
docs: docs,
});
});
});
app.get("/newReceipt", function (req, res) {
res.sendFile(__dirname + "/newReceipt.html");
});
app.post("/newReceipt", function (req, res) {
var t = new Transaction({
vehicleNumber: req.body.vehicleNumber,
date: getDate(),
time: getTime(),
tollAmount: req.body.amount,
});
t.save();
res.send("saved");
});
function getDate() {
var date = new Date();
date = date.toString();
console.log(date.substring(0, 10));
return date.substring(0, 10);
}
function getTime() {
var today = new Date();
return today.getHours() + ":"
+ today.getMinutes() + ":"
+ today.getSeconds();
}
app.listen(8080, function () {
console.log("Server listening on port 8080");
});
HTML代码
<!-- Newreceipt.html -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link href=
"https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity=
"sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossorigin="anonymous">
</head>
<body style="padding:1%;">
<div class="">
<h1>New Transaction</h1>
</div>
<form class="" action="/newReceipt" method="post">
<label for="">Vehicle Number</label>
<input type="text" name="vehicleNumber"
value="" style="margin-bottom:1%;">
<br>
<label for="">Amount</label>
<input type="number" name="amount"
value="" style="margin-bottom:1%;">
<br>
<button type="submit" name="button">
Add Transaction
</button>
</form>
</body>
</html>
结论: 现在我们可以使用这个应用记录过路车辆的交易,包括车牌号、日期、时间和过路费金额。除此之外,所有的过路费交易将会在我们的主页上统一列出。建立一个收费公路管理系统可以极大地提高收费、交通管理和整体道路安全的效率和效果。
极客教程