八、面向 ASP.NET 开发人员的 Node.js

JavaScript 已经成为最流行的语言之一,它不仅运行在客户端,而且也运行在服务器端。js 使 JavaScript 能够在服务器端运行,并提供非阻塞 I/O,这是一种事件驱动模型,使其更加轻量级、可扩展和高效。如今,它在执行实时操作、开发业务应用、数据库操作等方面的应用越来越广泛。Node.js 上的 JavaScript 可以与在 IIS 或任何其他 web 服务器上运行的 ASP.NET 相关联。

Node.js 简介

Node.js 是使用 JavaScript 构建服务器端应用的强大平台。Node.js 本身不是用 JavaScript 编写的,而是提供运行 JavaScript 代码的运行时环境。它允许在服务器端运行的 JavaScript 代码,提供运行在谷歌 V8JavaScript 引擎上的运行时,它是一个用 C++编写的开源 JavaScript 引擎,并由谷歌浏览器使用,在通过 V8JIT 编译器执行时将 JavaScript 代码编译成机器代码。

Node.js 在单个线程上工作;与其他为每个请求创建单独线程的服务器端技术不同,Node.js 使用事件回调系统,该系统使用单个线程处理请求。如果有多个请求到达,他们必须等待线程变得可用,然后获取它。在出现错误的情况下,Node.js 不会抛出错误,这是避免错误冒泡和单个线程中止的一项基本技术。如果在为请求提供服务时出现任何错误,Node.js 将在回调参数和响应本身中发送错误日志。这允许主线程传播错误并延迟响应。js 非常适合编写网络应用。它由 HTTP 请求、其他网络通信任务和使用 web 套接字的实时客户端/服务器通信组成。

Node.js web 服务器的请求处理

Node.js web 服务器维护一个有限的线程池来处理客户端请求。当请求到达服务器时,Node.js web 服务器将该请求放入事件队列。然后,在无限循环中工作的事件循环组件将拾取请求,并在请求空闲时对其进行处理。此事件循环组件是单线程的,如果请求涉及 I/O 阻塞操作,例如文件系统访问、数据库访问或其他操作,它将检查内部线程池中线程的可用性,并将请求分配给可用线程。否则,它将处理请求并一次性将响应发送回客户端。当内部线程完成 I/O 阻塞请求时,它首先将响应发送回事件循环,事件循环将响应发送回客户端。

Node.js 与.NET 的比较

ASP.NET 和 Node.js 都是服务器端技术。下图显示了 Node.js 与.NET 的比较:

Comparison of Node.js with .NET

NPM

节点包管理器NPM是用于安装节点模块的 Node.js 包管理器。js 提供了一种用 JavaScript 编写模块的方法,通过 NPM,我们可以在其他应用中添加和重用这些模块。在 ASP.NETCore 中,我们已经使用了一些模块,例如 Gulp 和 Grunt 来缩小 CSS 和 JavaScript 文件,并执行复制和合并操作。package.json文件是一个配置文件,用于保存项目中使用的应用和节点模块的元数据信息。以下是package.json文件的示例屏幕截图:

NPM

可以通过执行以下命令来安装依赖项:

npm install NAME_OF_THE_PACKAGE –save

例子:

npm install gulp –save

--save用于更新package.json依赖项部分并添加下载的包。

安装 Node.js

Visual Studio 为使用 Node.js 开发程序提供了强大的支持。要在 Windows 平台上配置 Node.js 开发环境,请从下载并安装 Node.jshttp://nodejs.org 。根据平台,有各种安装程序可用,如以下屏幕截图所示:

Installing Node.js

对于 Windows,我们将下载下载.msi软件包的 64 位 Windows 安装程序,并带您浏览一些简单的向导屏幕。您会注意到 Node.js 安装程序包含运行节点程序的运行时和引用程序中其他节点模块的 NPM。这可以在以下屏幕截图中看到:

Installing Node.js

环境路径中已经添加了npmnode等命令,我们可以直接从命令提示符执行这些命令。因此,如果我们打开命令提示符并写入node,它会给您节点提示符,允许您动态编写 JavaScript 代码并执行,如下图所示:

Installing Node.js

或者,我们也可以通过调用node javascriptfile.js来运行.js文件。

以下是对数组中定义的数字求和的样本example1.js文件:

console.log("NodeJs example");

var numbers= [100,20,29,96,55];

var sum=0;
for(i=0; i< numbers.length; i++)
{
 sum += numbers[i];
}
console.log("total sum is "+ sum);

以下为输出:

Installing Node.js

在 Visual Studio 2015 中使用 Node.js

市场上有很多集成开发环境IDE都有 Node.js 工具支持。IDE(如 Visual Studio Code、Sublime、Komodo 和 Node Eclipse)是使用 Node.js 的常用 IDE,但在实践中,大多数.NET 开发人员使用 Visual Studio IDE 时更为舒适和熟悉。因此,在本章中,我们将使用 Visual Studio 2015 社区版。

Node.js 模板可以通过安装其扩展安装在 Visual Studio 2015 中。可通过 Visual Studio 菜单选项工具扩展和更新安装扩展:

Using Node.js with Visual Studio 2015

Node.js 的这个扩展安装了各种模板,可以开始使用 Node.js 开发应用。有一个模板使用空白 No.js 控制台应用模板、使用 NoDE.JS Express 模板的 Web 应用开发控制台应用,等等:

Using Node.js with Visual Studio 2015

使用这些模板的基本优点是节省了手动配置的时间,这些模板通过提供基本的项目结构来帮助开发人员立即启动 Node.js 应用。

让我们首先创建一个基本控制台应用模板。基本控制台应用有一个包含节点包的npm文件夹,package.json包含元数据信息和其他配置属性,app.js包含实际 JavaScript 代码:

Using Node.js with Visual Studio 2015

Node.js 的此扩展提供了一个方便的功能,只需右键-点击npm文件夹并选择安装新 npm 软件包选项,即可添加节点模块,如以下屏幕截图所示:

Using Node.js with Visual Studio 2015

选择此选项后,Visual Studio 将打开一个窗口,帮助搜索任何节点包,并通过几次单击将其添加到应用中:

Using Node.js with Visual Studio 2015

上图显示了可通过此选项添加的Gulp包的版本。

交互窗口是 Visual Studio 中另一个不错的功能,它打开了集成在 Visual Studio 选项卡中的命令提示符,您可以立即编写 JavaScript 代码并执行命令,如下图所示:

Using Node.js with Visual Studio 2015

使用 VisualStudio 还有其他几个好处:可以使用 Git 或 TFS 版本存储库,调试代码并在 JavaScript 文件上启用断点,等等。Node.js 的 Visual Studio 特定项目文件称为.njsproj,位于项目的根文件夹中。

使用 Node.js 的简单控制台应用

Node.js 应用由一个或多个 JavaScript 文件组成,这些文件为应用提供特定功能。在一个 JavaScript 文件中编写数千行代码实际上是不可能的,而且还会增加可维护性问题。在 Node.js 中,我们可以创建多个 JavaScript 文件,并通过requireexport对象使用它们,这是通用 js 模块系统的一部分:

export: used to export variables, functions and objects 

//exportexample.js
module.exports.greeting = "Hello World";

require: To use the objects resides in different JavaScript files using require object. 

//consumerexample.js – referencing through file
var obj = require('./exportexample.js');

或者,我们也可以调用require,而不指定.js文件扩展名,它会自动加载存在于特定路径上的文件。如果路径对应于文件夹,则将加载所有 JavaScript 文件:

//consumerexample.js – referencing through file
var obj= require('./exportexample');

应用启动时的主入口点在package.json下定义。在下面的屏幕截图中,app.js是首先加载并由 Node.js 执行的主入口点文件:

Simple console application using Node.js

让我们实现一个基本示例,它有两个文件,即app.js(主条目)和cars.js,并返回car对象的一些属性,例如namemodelengine。首先,创建一个控制台应用项目并添加一个cars.js文件。

以下是cars.js的代码:

module.exports.cars = [
{name:"Honda Accord" , model:"2016", engine: "V6"}, 
{name:"BMW X6", model:"2015", engine: "V8"}, 
{name:"Mercedez Benz",model:"2016", engine:"V12"}
];

通过module.exports,我们可以导出任何对象。无论它是变量、函数还是 JSON 对象,都可以通过该函数导出。此外,导出的对象可以通过app.js中的require对象使用,如下代码所示:

var cars = require('./cars.js');
console.log(cars);

以下是输出:

Simple console application using Node.js

前面的代码显示了cars.js文件中定义的 JSON 输出。为了初始化cars对象,循环列表中定义的汽车项目,我们需要将其导出为函数,并通过this关键字进行定义。通过this指定它将使列表可以从我们在app.js文件中创建的 cars 对象访问。

以下是cars.js的修改版本:

module.exports = function () {
  this.carsList =   
  [
    { name: "Honda Accord" , model: "2016", engine: "V6" }, 
    { name: "BMW X6", model: "2015", engine: "V8" }, 
    { name: "Mercedez Benz", model: "2016", engine: "V12" }
  ];
};

下面是app.js文件的修改版本,该文件初始化了cars对象并在列表中循环:

var cars = require('./cars.js');
var c = new cars();
var carsList = c.carsList;
for (i = 0; i < carsList.length; i++) { 
  console.log(carsList[i].name);
}

带有 Node.js 的 Web 应用

有各种 Node.js web 框架可用。Express 和 Hapi.js 等框架是功能强大的框架,具有不同的体系结构和设计。在本节中,我们将对 web 和移动应用使用 Node.js 最广泛使用的 web 框架之一 Express framework,并提供开发 web应用编程接口API的应用框架模型。

创建空白 Node.js 应用

Visual Studio Node.js 的扩展提供了各种模板来开发 web 应用。我们将首先创建一个空白的 Node.js web 应用,如以下屏幕截图所示:

Creating blank Node.js applications

空白 Node.js 应用模板创建一个server.js文件,并将package.json文件中的main属性设置为加载server.jsserver.js 的内容如下:

Creating blank Node.js applications

第一条语句添加了 Node.js 的http模块的依赖关系,第二条语句是服务器监听 HTTP 请求的端口号,第三条语句在启动后使用http对象创建服务器并返回响应Hello World。当服务器启动时,可以通过调用http://localhost:1337进行请求。

前面的代码片段链接了实际侦听传入请求的listen()方法,并使用res.end()方法发送响应。或者,我们也可以使用res.write()方法指定返回的内容。以下是同一代码的更简化版本,以了解各部分是如何组合在一起的:

//Initialized http object
var http = require('http');

//declared port
var port = process.env.port || 1337;

//Initialized http server object and use res.write() to send actual response content
var httpServer= http.createServer(function (req, res) {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.write('Hello World\n');
    res.end();
});

//listening for incoming request
httpServer.listen(port);

在 Node.js 中使用 web 应用的 Express framework

在任何编程语言中,框架都有一个重要的优点,可以最大限度地减少开发 web 应用所需的工作量。该框架在处理请求方面起着重要作用,例如加载特定视图、将模型注入视图等。与 ASP.NET 一样,w 有两个 web 应用框架,即 ASP.NET web 窗体和 ASP.NET MVC,Node.js 提供 Express EJS、Jade 和许多其他 web 应用框架来构建健壮的 web 应用。

扩展 simple Node.js 使用 Express

通过 Visual Studio 的 Node.js 扩展,您可以获得所有模板,开始使用 Express 3.0 和 Express 4.0 应用框架。Express 4.0 是最新版本,具有一些新功能和改进。我们可以使用为您引导大部分配置级别内容的模板,但为了更清晰,我们将扩展前面创建的 simple Node.js 示例,并使用 Express framework 在其上开发一个简单的 web 应用。

要使用Express,我们必须使用 NPM 添加其包依赖关系,如以下屏幕截图所示:

Extend simple Node.js to use Express

添加 Express 软件包后,可以添加以下代码段启动 Express 应用:

//Initialized http object
var http = require('http');

//adding express dependency
var express = require('express');

//creating express application
var expressApp = express();

//Configuring root call where '/' represents root path of the URL
expressApp.get("/", function (req, res) {
    res.send("<html><body><div>Hello World</div></body></html>");
});

//declared port
var port = process.env.port || 1337;

//Initialized http server object and use res.write() to send actual response content
var httpServer = http.createServer(expressApp);

//listening for incoming request
httpServer.listen(port);

这是一个返回 HTML 内容的简单Hello World示例。现在,在我们希望返回特定视图而不是静态 HTML 内容的场景中,我们通过使用 Express view 引擎来实现这一点,下面将讨论这一点。

快速查看引擎

Express 有各种视图引擎,但 Jade 和 EJS 是使用最广泛的。我们将逐一检查这些,看看有什么不同。

EJS 视图引擎

在 EJS 视图引擎中,视图是 HTML 页面,可以使用 scriptlets:<% %>绑定模型属性。

要使用 EJS 启动,我们需要通过 Visual Studio 中的 NPM package manager 选项或通过执行npm install ejs –save命令来添加 EJS 包:

EJS view engine

添加了后,我们可以将视图引擎设置为ejs,如下代码段所示:

//Initialized http object
var http = require('http');

//adding express dependency
var express = require('express');

//creating express application
var expressApp = express();

//Set jade for Node.js application
expressApp.set('view engine', 'ejs') 

通过调用响应对象render()方法设置ejs视图的路径,如下代码所示:

//Configuring root call where '/' represents root path of the URL
expressApp.get("/", function (req, res) {
    res.render("ejsviews/home/index");
});

index.ejs文件添加到home文件夹中。所有视图都应该位于 rootViews文件夹下,否则在应用运行时将不会加载它们。因此,应在Views文件夹下定义ejsviews文件夹,并在ejsviews文件夹内定义home,如下图所示:

EJS view engine

以下是应用启动时将呈现的 EJS 视图的内容:

<html>
 <body>
  <div> <h1> This is EJS View </h1> </div>
 </body>
</html>

在创建侦听端口号为1337的请求的服务器的ejsserver.js文件底部添加代码:

//declared port
var port = process.env.port || 1337;

//Initialized http server object and use res.write() to send actual response content
var httpServer = http.createServer(expressApp);

//listening for incoming request
httpServer.listen(port);

当应用运行时,index.ejs将被加载并呈现 HTML 内容,如下图所示:

EJS view engine

我们还可以在 JSON 对象的表示中传递模型。假设我们需要传递应用名称和描述;我们可以在调用响应对象的render()方法时传递这些值,如下代码所示:

//Configuring root call where '/' represents root path of the URL
expressApp.get("/", function (req, res) {
    res.render("ejsviews/home/index", { appName: "EJSDemo", message: "This is our first EJS view engine example!" });
});

index.ejs中,我们可以使用 Scriptlet 将这些值与 HTML 控件结合使用:

<html>
 <body>
   <h1> <%= appName %> </h1>
  <p> <%= message %></p>
 </body>
</html>

EJS 还支持包含静态内容的布局页面,例如 web 应用的页眉和页脚。因此,开发人员不需要在每个页面上反复定义主布局内容,我们可以将其集中化,就像我们在 ASP.NET MVC 中使用 ASP.NET web 表单中的_layout.cshtmlSite.master一样。

要使用母版页,我们需要再添加一个名为ejs-local的包。可以使用 Visual Studio 中的 NPM 软件包管理器窗口添加此软件包,也可以将npm命令作为npm install ejs-local --save运行:

EJS view engine

在添加这个包之后,我们可以添加ejs-locals,如下代码所示。在设置视图引擎之前,必须先设置:

//Initialized http object
var http = require('http');

//adding express dependency
var express = require('express');
var ejsLocal = require('ejs-locals');
//creating express application
var expressApp = express();

//Add engine that supports master pages
app.engine('ejs', ejsLocal);

在同一ejsviews文件夹中添加layout.ejs页面,并指定 HTML 内容:

<html>
<head>
  <title> <%= appName %> </title>
</head>
<body>
  <%= body %>
</body>
</html>

子页面的内容将在布局页面中定义主体的行上呈现,如前代码所示。以下是index.ejs文件的代码片段:

<% layout('../layout.ejs') -%>
<h1><%= appName %></h1>
<p> <%= message %></p>

生成以下输出:

EJS view engine

玉景引擎

Jade 视图引擎是另一个 Node.js 视图引擎,在定义视图时,语法与我们在 EJS 中看到的非常不同。要开始使用 Jade view 引擎,我们需要使用 NPM 安装 Jade view 引擎节点包。我们可以从 Visual Studio NPM 软件包管理器或通过运行npm install jade –save命令来安装:

Jade view engine

当您安装它时,它会在package.json依赖项部分添加 Jade 包。我们首先在app.js文件中设置 Jade view 引擎(启动 Node.js 项目的主要入口点)。

以下是在 app.js中设置 Jade view 引擎的代码:

//adding express dependency
var express = require('express');

//creating express application
var expressApp = express();

//Set jade for Node.js application
expressApp.set('view engine', 'jade');

您会注意到我们没有通过require对象指定 Jade 引用。这是因为当加载 Express 框架时,它将自动注册 Jade 的依赖项。以下代码段加载 Jade 视图:

//Configuring root call where '/' represents root path of the URL
expressApp.get("/", function (req, res) {
res.render("home/index", 
{ 
appName: "JadeDemo",   
message: "This is our first Jade view engine example!"
}
);
});

Jade 视图语法通常不同于 HTML,所有视图扩展都应该是.jade。在前面的代码中,我们将指向index.jade,这里不需要明确指定 Jade。Index.jade应位于views/home文件夹下。让我们创建一个名为views的文件夹,然后创建其中的home文件夹。添加一个新的 Jade 文件并将其命名为index.jade。以下是在 HTML 元素中显示appNamemessage的代码:

doctype
html
    body
        h1= appName
        p= message

使用 Jade 语法,您不必定义完整的 HTML 标记,只需通过它们的名称指定它,然后是分配给它们的值。例如,在前面的示例中,我们通过 response render()方法设置作为 JSON 对象传递给该视图的appNamemessage值。但是,HTML 元素还支持许多属性,例如设置控件宽度、字体颜色、字体样式等。在后面的部分中,我们将看到如何在 Jade 中实现这一点。

仅当绑定到视图中的任何值时,才需要使用等于(=运算符。如果要指定硬编码静态值,则无需使用“等于”运算符即可轻松设置,如以下代码所示:

doctype
html
    body
        h1 Jade App
        p This is Jade View

以下是一些在特定于 HTML 的场景中使用 Jade 语法的示例:

|

属性

|

|

HTML

| | --- | --- | --- | | 文本框 |

input(type='text' name='txtName')

|

<input type='text' name='txtName'/>

| | 锚定标签 |

a(href='microsoft.com') Microsoft

|

<a href="microsoft.com">Microsoft</a>

| | 复选框 |

input(type='checkbox', checked)

|

<input type="checkbox" checked="checked"/>

| | 带有样式属性的锚定 |

a(style = {color: 'green', background: 'black'})

|

<a style="color:green;background:black"></a>

| | 链接按钮 |

input(type='button' name='btn')

|

<input type="button" name="btn"/>

|

您可以在这里了解更多关于玉石语言的信息:http://jade-lang.com/

Jade 的框架还支持布局页面。版面页面包含网站的静态信息,主要放在页眉、页脚或侧边栏中,内容实际上会根据请求的页面进行更改。在 ASP.NETWeb 表单中,我们使用<asp:ContentPlaceHolder>标记定义母版页,这些标记将页面内容引用到该母版页。在 ASP.NET MVC 中,这可以使用 Razor@RenderBody元素来完成。在 Jade 中,我们可以使用block关键字后跟块的名称来定义内容块。例如,下面是包含block contentBlock语句的layout.jade,其中block表示子页面内容呈现的位置,contentBlock是必须在子页面中定义的块的名称。也可以在单个视图中定义多个块。

以下是布局页面的内容:

doctype html
html
  head
    title Jade App
  body
  block contentBlock

布局页面可以使用extends关键字,后跟布局页面名称。Jade view 引擎会自动搜索具有该名称的页面,如果找到该页面,则会搜索块名称并将内容放在其中。下面是使用布局页面layout.jade的子页面index.jade

extends layout
block contentBlock
        h1= appName
        p= message

输出结果如下:

Jade view engine

Express 应用中的路由

我们现在已经学习了 EJS 和 Jade view 引擎的基础知识。两者都提供类似的功能,但语法不同。在前面的示例中,我们发送了一个响应,该响应指向在客户端呈现内容的特定页面。

Express 框架提供了几种与 HTTP 方法相对应的方法,如getpostputdelete等。我们可以使用get方法检索某物、post创建记录、put更新等等。页面可以驻留在Views文件夹中的任何位置,但路由实际上定义了在特定 URL 路径上发出请求时必须加载的页面。

让我们在Views/ejsviews/home文件夹中创建一个 EJS 页面,并将其命名为about.ejs

可以使用 Express 应用对象定义路由,如下代码所示:

expressApp.get("/About", function (req, res) {
    res.render("ejsviews/home/about");
});

当用户浏览到http://localhost/About时,显示关于页面。

中间件

Node.js Express 还提供了一种特殊的路由方法all(),它没有映射到任何 HTTP 方法。但它用于在路径上加载中间件,而与请求的 HTTP 方法无关。例如,在http://localhost/middlewareexample进行 HTTPGETPOST请求时,将执行与以下代码相同的all()方法:

expressApp.all('/middlewareexample', function (req, res) {
    console.log('Accessing the secret1 section ...');
});

就像在.NET 中一样,我们有 OWIN 中间件,可以链接到请求管道中。同样,Node.js Express 中间件也可以链接起来,通过调用下一个中间件调用,只需稍微更改函数签名即可。以下是修改后的版本,它在响应对象之后使用next参数,该响应对象为管道中的下一个中间件提供处理程序,按照特定请求路径的顺序定义:

expressApp.all('/middlewareexample', function (req, res, next) {
    console.log('Accessing the secret1 section ...');
    next();
});

例如,假设我们有两个中间件,第一个中间件只将信息记录到控制台窗口,而第二个中间件将 HTML 内容返回给客户端。以下是包含 EJS 视图引擎中两个中间件的server.js 文件:

//Initialized http object
var http = require('http');
//adding express dependency
var express = require('express');

//creating express application
var expressApp = express();

expressApp.all('/middlewareexample', function (req, res, next) {
    console.log('Middleware executed now calling next middleware in the pipeline');
    next(); // pass control to the next handler
});
expressApp.all('/middlewareexample', function (req, res) {
    res.send("<html><body><div>Middleware executed</div></body></html>");    
});

//declared port
var port = process.env.port || 1337;

//Initialized http server object and use res.write() to send actual response content
var httpServer = http.createServer(expressApp);

//listening for incoming request
httpServer.listen(port);

现在当我们访问 URL 路径http://localhost/middlewareexample时,消息将打印在控制台上,并在浏览器中呈现 HTML 内容:

Middleware

以下是将在浏览器中呈现的 HTML 内容:

Middleware

带 Express 框架的 MVC

几乎每个应用都由许多页面组成,在主server.js上定义所有逻辑和路由既不实用也不可维护。在本节中,我们将看到如何使用 Express 框架实现模型视图控制器MVC模式)。我们将开发一个简单的应用,以了解如何开发控制器和数据服务,以及控制器如何使用 Express 框架加载视图和注入模型。

MVC 模式

MVC 是一种软件架构模式,用于分离应用的关注点。模型表示包含用于保存信息的属性的实体,而控制器用于将模型注入视图并加载视图。控制器还用于将模型存储在数据库中,而视图是呈现控制器注入的模型并在需要时使用它的页面。

创建控制器

我们将首先创建一个简单的homeController来呈现主页。让我们扩展上面开发的 EJS 视图引擎示例,并在项目的根目录下创建一个Controllers文件夹。在Controllers文件夹中,创建一个HomeController.js文件,并将以下代码片段放在那里:

(function (homeController) {
    homeController.load = function (expressApp) {
        expressApp.get('/', function (req, res) {
            res.render("ejsviews/home/index", {appName: "EJS Application", message:"EJS MVC Implementation"})
        });
    };
})(module.exports);

在前面的代码中,有一个匿名 JavaScript 函数,它接受module.export对象,并在执行时将其绑定到homeController。以这种方式实现它的基本优点是,使用homeController对象定义的每个方法或属性都可以被调用对象导出和访问。在前面的示例中,我们定义了一个load()方法,该方法定义根路径(/处的路由,并将索引页面返回给客户端。

ejsserver.js主文件中,我们可以使用require对象来使用控制器,如下代码所示:

//Initialized http object
var http = require('http');

//adding express dependency
var express = require('express');

//adding ejs locals
var ejsLocal = require('ejs-locals');

//creating express application
var expressApp = express();

//Add engine that supports master pages
expressApp.engine('ejs', ejsLocal);

//Set jade for Node.js application
expressApp.set('view engine', 'ejs');

//Initializing HomeController
var homeController = require('./Controllers/HomeContoller.js');
homeController.load(expressApp);

//declared port
var port = process.env.port || 1337;

//Initialized http server object and use res.write() to send actual response content
var httpServer = http.createServer(expressApp);

//listening for incoming request
httpServer.listen(port);

在前面的代码中,我们使用require对象添加了HomeController对象,并调用load()方法来定义网站运行时导航到索引页的路由。

创建数据服务

每个业务应用都涉及大量 CRUD(创建、读取、更新和删除)操作。为了更好的设计,这些操作可以在数据服务对象中单独实现,因此如果多个控制器想要使用相同的服务,它们可以使用它们,而无需重复编写相同的代码。在本节中,我们将创建一个数据服务 JavaScript 文件,用于读取数据并将其传递给路由函数。首先,让我们在应用的根目录下创建一个文件夹名DataServices,并在其中创建ProductService.js。以下是ProductService.js的代码,返回产品数组:

(function(data){
    data.getProducts = function () {
        return [{
                name: 'Product1',
                price: 200,
            }, 
            {
                name: 'Product2',
                price: 500
            },
            {
                name: 'Product3',
                price: 1000
            }
        ];
    };
})(module.exports);

我们可以通过require对象在HomeController内部使用此ProductService

(function (homeController) {
    var productService = require('../DataServices/ProductService');

    homeController.load = function (expressApp) {
        expressApp.get('/', function (req, res) {
            var products = productService.getProducts();
            res.render("ejsviews/home/index", { appName: "EJS Application", message: "EJS MVC Implementation", data: products });
        });
    };
})(module.exports);

这里是index.ejs文件,它循环遍历产品并显示产品名称和价格:

<% layout('../layout.ejs') -%>
<h1><%= appName %></h1>

<p> <%= message %></p>

<div>

 <% data.forEach(function(product) { %>
   <li><%= product.name %> - <%= product.price %></li>
 <% }); %>

</div>

最后,输出如下所示:

Creating data services

在 Node.js 中访问 Microsoft SQL server

Node.js 提供不同的数据库驱动程序,可以作为节点包添加。有用于 MongoDB 驱动程序、Microsoft SQL Server 驱动程序等的软件包。我们将使用 Node.js 的 MS SQL 驱动程序连接 Microsoft SQL server 数据库。要安装mssql,您可以运行npm install mssql –save命令,或者从 NPM package manager 窗口添加它,如以下屏幕截图所示:

Accessing the Microsoft SQL server in Node.js

提示

使用 MSSQL 驱动程序,应为相应的 SQL server 实例启用 TCP/IP。

从 Microsoft SQL server 数据库读取记录

在的DataService.js文件中,我们将添加getProducts()方法,从 SQL Server 数据库加载产品列表。

以下是getProducts()方法,它接受回调函数,所以一旦从数据库中取出产品列表,就会在调用方的回调函数中传递:

(function(data){
data.getRecords = function (callbackFn) {
        //loaded SQL object
        var sql = require('mssql');

        //database configuration attributes to connect
        var config = {
            user: 'sa',
            password: '123',
            server: 'ovais-pc', // You can use 'localhost\\instance' to connect to named instance 
            database: 'products'
        }

        var products = null;
        //Connect to SQL Server returns a promise and on successfull connection executing a query using Request object
        sql.connect(config).then(function () {
            new sql.Request().query('select * from products', function (err, recordset) {      
                callbackFn(recordset);        
            });
        });

     };
})(module.exports);

在前面的代码中,我们使用require对象初始化了sql对象。Config变量包含连接属性,如usernamepasswordserverdatabase。这是在调用sql connect()方法时传递的。Connect()方法返回一个then()承诺,通过这个承诺,我们可以使用sql.Request()方法启动 SQL 查询请求。如果请求成功,我们将在recordset对象中获得结果集,该结果集将通过其回调函数返回给调用者。

下面是HomeController.js的修改版本,它调用DataService``getRecords()方法,并将检索到的产品列表作为模型传递给索引视图:

(function (homeController) {
    var productService = require('../DataServices/ProductService');

    homeController.load = function (expressApp) {
        expressApp.get('/', function (req, res) {
            var products = productService.getRecords(function (products) {
                console.dir(products);
                res.render("ejsviews/home/index", { appName: "EJS Application", message: "EJS MVC Implementation", data: products });
            });
        });
    };
})(module.exports);

下面的是index.js文件,它循环浏览产品列表并显示产品名称和价格:

<% layout('../layout.ejs') -%>
<h1><%= appName %></h1>
<p> <%= message %></p>

<table>
<th> 
<td> Product Name </td>
<td> Description </td>
<td> Price </td>
</th>
 <% data.forEach(function(product) { %>
  <tr> <td><%= product.Name %> </td> <td> <%= product.Description %> </td><td> <%= product.Price %> </td></tr>
 <% }); %>
</table>

在 Microsoft SQL server 数据库中创建记录

为了在数据库中创建记录,我们可以定义包装在 HTML 表单标签中的 HTML 输入元素,在提交表单时,我们可以通过在HomeController.js文件中定义post方法发出 post 请求。提交表单时,可以使用request.body对象检索值。这是一个解析器,它解析 DOM 并生成一个包装在 form 标记下的元素列表。我们可以像req.body.txtName一样访问它,其中txtName是 HTML 输入元素,req是请求对象。

Express 4.0 将body-parser对象拆分成一个单独的包,可以使用npm install body-parser –save命令或通过 NPM package manager 窗口单独下载,如下图所示:

Creating a record in the Microsoft SQL server database

在主ejsserver.js文件中,使用require对象添加body-parser并通过调用expressApp,use()方法将其传递到expressApp对象中:

var bodyParser = require('body-parser');

expressApp.use(new bodyParser());

添加后,我们可以修改HomeController.js并定义一个POST方法,表单提交后会调用该方法:

    expressApp.post('/', function (req, res) {
            console.log(req.body.txtName);
           productService.saveProduct(req.body.txtName, req.body.txtDescription, req.body.txtPrice, function (result) {
                res.send("Record saved successfully");
            });
        });

前面的方法调用数据服务saveProduct()方法,并将用户填写的值作为参数传递。最后一个参数是回调函数,如果记录保存成功,将执行回调函数。以下是DataService.js文件的saveProduct代码片段:

data.saveProduct = function (name, description, price, callbackFn) {

        //loaded SQL object
        var sql = require('mssql');

        //database configuration attributes to connect
        var config = {
            user: 'sa',
            password: '123',
            server: 'ovais-pc', // You can use 'localhost\\instance' to connect to named instance 
            database: 'products'
        }

        //Connect to SQL Server returns a promise and on successfull connection executing a query using Request object
        sql.connect(config).then(function () {
            new sql.Request().query("INSERT into products (Name, Description, Price) values('"+ name +"', '"+ description+"',"+ price+")", function (err, recordset) {
                callbackFn(recordset);
            });
       });

    };

最后,这里是Index.ejs视图,它包含一个带有NameDescriptionPrice字段的表单:

<form method="post">
<table>
<tr>
  <td> Product Name: </td>
  <td> <input type='text' name='txtName'  /> </td>
</tr>
<tr>
  <td> Description: </td>
  <td><input type='text' name='txtDescription'  /></td>
</tr>
<tr>
  <td> Price: </td>
  <td><input type='number' name='txtPrice' /></td>

</tr>
<tr>
<td> &nbsp; </td>
<td><input type="submit" value="Save" /> </td>
</tr>
</table>
</form>

要了解更多关于mssql节点包的,请使用此链接:https://www.npmjs.com/package/mssql

总结

本章重点介绍 Node.js 的基础知识,以及如何在使用 JavaScript 开发服务器端应用时使用它们。我们已经学习了两个视图引擎 EJS 和 Jade,以及如何使用它们。我们还学习了如何使用控制器和服务来实现 MVC 模式。最后,我们通过查看访问 Microsoft SQL server 数据库以在数据库上执行、创建和检索操作的示例来结束本文。在下一章中,我们将重点介绍在大规模应用中使用 JavaScript 的最佳实践。