比较Node.js Web框架Hapi和Express|Hapi和Express的比较

 

接触过Hapi框架你可能想知道它与Node.js开发中的Express Web框架相比如何。在本文中,我们将比较这两个框架,并探讨我们在开发中的经验差异

 

比较Node.js Web框架Hapi和Express|Hapi和Express的比较

Hapi与Express之间的异同点

 Express 和 Hapi 的目标都是高度灵活、简单和可扩展。这种相似性意味着两者都有易于使用的api,它们都是高度模块化的,并且可以在应用程序可能变得非常大时支持它

这些框架的学习起来非常容易上手,因为它们非常直观,不像固执己见的框架。如果你已经使用过Express,应该能够快速上手Hapi,反之亦然

然而,这两个框架之间存在一些差异,我们将在本文中对此进行描述

Hapi.js默认包含比Express更多的“电池”。例如,在使用Express解析表单到通过“POST”请求的有效负载时,必须包含body-parser中间件。然后使用它解析POST有效负载并使用来自用户的数据。另一方面,对于Hapi,则不需要中间件。负载由框架本身为你解析,你可以直接在请求对象上访问负载

 

基本的服务器Server和路由Routing

首先,我们将创建hapiapp,这是一个展示基本Hapi功能的示例Hapi应用程序。我们还将创建expressapp,它是使用Express的hapiapp的镜像,因此我们可以并排比较这些框架

两个应用程序都将使用ES6 JavaScript

创建两个目录,hapiapp和expressapp

在每个命令中,运行以下命令:

$ npm init

然后按“Enter”接受所有默认设置。这将在每个目录中创建 package.json 文件,从而创建两个不同的应用程序hapiapp和expressapp

首先,看看在Hapi.js中基本路由是如何工作的。在hapiapp目录中,通过运行以下命令安装Hapi模块:

$ npm install hapi@16.4.3 --save

然后创建一个index.js文件,内容如下:

// hapiapp/index.js

const Hapi = require('hapi');

// 创建 server
const server = new Hapi.Server();

// 配置 port
server.connection({  
    port: 8000,
    host: 'localhost'
});

// 基础 routes
server.route({  
    method: 'GET',
    path: '/',
    handler: (request, reply) => reply('Hello World')
});

server.route({  
    method: 'GET',
    path: '/hello/{name}',
    handler: (request, reply) => reply(`Hello ${request.params.name}`)
});

// 开启 server
server.start((err) => {  
    if (err) {
        console.error(err);
    }

    console.log('App running on port 8000...');
});

这个基本的应用程序在localhost:8000和localhost:8000/hello/<name>上创建两条路由。第一个将向用户输出一个简单的“Hello World”。而第二个是一个动态路由,它输出“Hello”,并在第二个“/”斜杠后输出我们在浏览器中输入的名称

在hapiapp目录下,使用以下命令运行应用程序,并尝试访问上面给出的url:

$ node index.js

 

类似的Express应用程序涉及非常类似的设置。在我们的expressapp目录中,使用以下命令安装Express:

$ npm install express@4.16.2 --save

然后创建一个index.js文件,内容如下:

// expressapp/index.js

const express = require('express');

// 创建 app
const app = express();

// 基础 routes
app.get('/', (req, res) => res.send('Hello World!'));  
app.get('/hello/:name', (req, res) => res.send(`Hello ${req.params.name}`));

// 开启 server
app.listen(3000, () => console.log('App running on port 3000...'));

这实现了与Hapi应用程序相同的功能。要在我们的expressapp目录中运行它,请运行以下命令:

$ node index.js

访问上面给出的路由,只不过现在我们运行的是localhost:3000,而不是localhost:8000

这两个代码示例都非常简单。服务器设置非常类似,不过Express在这里看起来更紧凑

 

中间件Middleware的使用

“中间件”是指在响应中将最终输出返回给用户之前,连续处理HTTP请求的软件模块的名称

中间件可以作为我们在应用程序中定义的函数,也可以作为在第三方中间件库中定义的函数

Express允许你附加中间件来处理每个请求。而Hapi与提供中间件功能的插件一起工作

中间件的一个例子是CSRF令牌,它有助于防止跨站点请求伪造(CSRF)黑客攻击。如果没有CSRF令牌,攻击者可以模拟合法请求并窃取数据或在应用程序上运行恶意代码

Hapi和Express处理CSRF攻击的方法类似。它涉及在服务器上为每个向服务器提交数据的表单生成一个秘密令牌。然后,服务器检查每个POST请求以获得正确的、难以伪造的令牌。如果请求不存在,服务器将拒绝该请求,从而避免恶意代码执行尝试。在这种情况下,中间件将为每个请求生成一个CSRF令牌,因此你不必在应用程序代码中执行它

这两个框架之间的区别主要是修饰性的,Hapi使用一个插件 Crumb 来生成和处理令牌。另一方面,Express使用名为 csurf 的中间件来生成和处理CSRF令牌

下面是一个生成Crumb令牌的Hapi代码示例:

'use strict';

const Hapi = require('hapi');  
const Vision = require('vision');

const server = new Hapi.Server({  
    host: '127.0.0.1',
    port: 8000
});

const plugins = [  
    Vision,
    {
        plugin: require('../'),
        options: {
            restful: true
        }
    }
];

// 添加 Crumb 插件
(async () => {
    await server.register(plugins);

    server.route([
        // 任何请求都应该设置一个“crumb”cookie
        // 对于跨源请求,将CORS“凭证”设置为true
        // 可以像这样创建返回面包屑的路由

        {
            method: 'GET',
            path: '/generate',
            handler: function (request, h) {

                return {
                    crumb: server.plugins.crumb.generate(request, h)
                };
            }
        },

        // 此路由的请求中必须设置具有crumb值的请求头“X-CSRF-Token”

        {
            method: 'PUT',
            path: '/crumbed',
            handler: function (request, h) {

                return 'Crumb route';
            }
        }
    ]);

    await server.start();

    console.log('Example restful server running at:', server.info.uri);
})();

和使用csurf的大致等价的Express代码:

const cookieParser = require('cookie-parser')  
const csrf = require('csurf')  
const bodyParser = require('body-parser')  
const express = require('express')

// 设置路由中间件
let csrfProtection = csrf({ cookie: true })  
let parseForm = bodyParser.urlencoded({ extended: false })

// 创建 express app
let app = express()

// 解析 cookies
// 我们需要这个,因为“cookie”在csrfProtection中是正确的
app.use(cookieParser())

app.get('/form', csrfProtection, function (req, res) {  
    // 将csrfToken传递给视图
    res.render('send', { csrfToken: req.csrfToken() })
});

app.post('/process', parseForm, csrfProtection, function (req, res) {  
    res.send('data is being processed')
});

中间件库还可以帮助你实现其他常见功能,如身份验证。你可以在这个中间件目录中找到一些Express中间件的列表。对于Hapi,有一个类似的Hapi插件库

 

图像和静态资源服务

在Hapi和Express之间提供静态文件非常类似。Hapi使用Inert插件,Express使用express.static内置的中间件

在我们的hapiapp应用程序中,我们可以通过在hapiapp/public上创建一个目录并在其中放置一个文件runsagainstbulls.jpg来提供来自公共目录的静态文件。

这是图像文件。右键单击并将其下载到应用程序中

比较Node.js Web框架Hapi和Express|Hapi和Express的比较 - 豆豆网

需要安装inert@4.2.1使用:

$ npm install inert@4.2.1 --save

现在我们可以使用以下代码来提供静态文件。在hapiapp/index.js中启动服务器的行之前添加这个

// hapiapp/index.js

...

// 使用inert服务静态文件
//此路径需要inert
server.register(require('inert'), (err) => {  
    if (err) {
        throw err;
    }

    // 从公共文件夹中为runswithbulls图像提供服务
    server.route({
        method: 'GET',
        path: '/image',
        handler: (request, reply) => {
            reply.file('./public/runsagainstbulls.jpg');
        }
    });
});

再次运行服务器并访问localhost:8000/image, 你将看到返回给你的图片

比较Node.js Web框架Hapi和Express|Hapi和Express的比较 - 豆豆网

在Express中,设置要少一些。只需将相同的图片复制到expressapp内新创建的公共文件夹。然后在启动服务器之前将这行代码添加到index.js文件中:

// expressapp/index.js

...

// 提供公用文件夹中的静态文件
app.use(express.static('public'));

启动服务器并访问localhost:3000/runsagainstbull .jpg,你将看到由Express提供的图片

比较Node.js Web框架Hapi和Express|Hapi和Express的比较 - 豆豆网

默认情况下,Express将为配置文件夹中的所有文件提供服务,Hapi也可以配置为Inert文件,尽管它不是默认设置

 

模板引擎Template Engines的使用

Hapi和Express都可以像使用HandlebarsTwig、EJS等引擎一样呈现和提供模板

在我们的hapiapp文件夹中,安装 vision plugin ,使用:

$ npm install vision@4.1.1 --save

还安装Handlebars模板引擎通过:

$ npm install handlebars@4.0.10 --save

然后,让我们在一个新目录模板中创建一个Handlebars模板,并在该目录中创建一个名为cat.html的新文件。我们将创建一个猫的列表,并使用这个模板显示它们

更新cat.html如下:

<!-- hapiapp/templates/cats.html -->  
<h2>All My Cats</h2>

<ol>  
    {{#each cats}}
        <li>{{name}}</li>
    {{/each}}
</ol>

此Handlebars模板循环遍历一组cat对象,并呈现每个cat的名称

在index.js中,在启动服务器之前,添加以下代码,这些代码配置vision并在/cats上创建路由,并在那里显示猫的列表

// hapiapp/index.js

...

// 配置 vision去渲染 Handlebars 模版
server.register(require('vision'), (err) => {  
    if (err) {
        throw err;
    }

    server.views({
        engines: {
            html: require('handlebars'),
        },
        path: __dirname + '/templates'
    });
});

// 显示 cats在/cats通过使用猫handlebars 模版
server.route({  
    method: 'GET',
    path: '/cats',
    handler: (request, reply) => {

        reply.view('cats', {
            cats: [
                {name: "Blinky the Cat"},
                {name: "Sammy the Happy Cat"},
                {name: "Eto the Thug Cat"},
                {name: "Liz a quiet cat"},
            ]
        });
    }
});

再次启动服务器并访问localhost:8000/cats,将看到页面上显示的猫列表

比较Node.js Web框架Hapi和Express|Hapi和Express的比较 - 豆豆网

要在Express中复制此功能,首先在expressapp根目录中创建一个名为views的目录。然后创建一个Handlebars文件cats.hbs内容,和Hapi差不多

<!-- expressapp/views/cats.hbs -->  
<h2>All My Cats</h2>

<ol>  
    {{#each cats}}
        <li>{{name}}</li>
    {{/each}}
</ol>

现在,在expressapp安装Handlebars引擎的Express:

$ npm install hbs@4.0.1 --save

然后要呈现模板并显示猫,请更新索引。在启动服务器之前添加以下代码:

// expressapp/index.js

...

// 使用handlebars 作为view 引擎
app.set('view engine', 'hbs');

// 显示 cats列表 at /cats
app.get('/cats', function (req, res) {

    res.render('cats', {
        cats: [
            {name: "Blinky the Cat"},
            {name: "Sammy the Happy Cat"},
            {name: "Eto the Thug Cat"},
            {name: "Liz a quiet cat"},
        ]
    });
});

重新启动Express服务器,并访问localhost:3000/cats,我们将看到与Hapi相同的猫列表

这里我们注意到,Hapi示例比Express示例需要更多的配置。不过,这两个代码示例都很容易理解。在这样一个小应用程序中,在这两个框架之间几乎没有什么可选择的。而在较大的应用程序中,会有更明显的差异。例如,Hapi以涉及比Express更多的样板代码而闻名。另一方面,它也比Express为你提供了更多的开箱即用功能,因此肯定存在一些权衡

 

数据库的连接

MongoDB是一个经过实战测试的NoSQL数据库,可以在你的应用程序中使用。通过mongoose对象建模库,我们可以将MongoDB连接到Hapi和Express应用程序。对于这一部分,如果你还没有安装MongoDB,则需要在系统上安装并运行它

为了了解这两种框架是如何工作的,现在让我们更新示例应用程序以将cat对象存储在数据库中。同样,我们的cat对象列表数据将从数据库中获取

我们必须遵循的步骤是:

  1. 设置到本地主机的MongoDB连接
  2. 定义一个mongoose模式
  3. 创建对象
  4. 存储对象
  5. 从数据库中检索文档

回到我们的hapiapp目录,安装mongoose通过:

$ npm install mongoose@4.11.1 --save

因为我们希望能够从/cats视图中创建新猫,所以更新cats模板以包含新猫的表单。在hapiapp/templates/cats.html中的猫列表上面添加以下几行

<!-- hapiapp/templates/cats.html -->

...

<form class="" action="/cats" method="post">  
    <input type="text" name="name" placeholder="New cat">
</form>

现在我们可以在index.js文件的顶部导入mongoose。在导入Hapi的行下面,添加以下行:

// hapiapp/index.js

...

const Hapi = require('hapi');  
const mongoose = require('mongoose');

// 连接 MongoDB
mongoose.connect('mongodb://localhost/hapiappdb', { useMongoClient: true })  
    .then(() => console.log('MongoDB connected'))
    .catch(err => console.error(err));

// 创建可以持久化到数据库的Cat模型
const Cat = mongoose.model('Cat', {name: String});

现在更新/cats的“GET”路由,以便从MongoDB动态获取猫。我们还将添加一个新的“POST”路由,在这里我们可以将猫保存到数据库中。下面是进行这些更改的代码,替换了原来的GET /cats代码:

// hapiapp/index.js

...

// 使用猫handlebars模板在/cats上显示猫
server.route({  
    method: 'GET',
    path: '/cats',
    handler: (request, reply) => {

        let cats = Cat.find((err, cats) => {
            console.log(cats);
            reply.view('cats', {cats: cats});
        });
    }
});

// post cats
server.route({  
    method: 'POST',
    path: '/cats',
    handler: (request, reply) => {
        let catname = request.payload.name;
        let newCat = new Cat({name: catname});

        newCat.save((err, cat) => {
            if (err) {
                console.error(err);
            }

            return reply.redirect().location('cats');
        });
    }
});

现在,当你再次运行服务器并访问localhost:8000/cats时,将看到cat列表现在是空的。但是有一个表单,你可以输入新的猫名并按enter键,你输入的猫将被保存到MongoDB。输入一些猫,你会得到一个猫的列表,如下所示:

比较Node.js Web框架Hapi和Express|Hapi和Express的比较 - 豆豆网

对于Express,安装和设置Mongoose几乎是一样的。回到expressapp,安装Mongoose:

$ npm install mongoose@4.11.1 --save

导入Express后,在index.js中添加Mongoose连接代码和Cat模型

// expressapp/index.js

...

const express = require('express');  
const mongoose = require('mongoose');

// 连接 MongoDB
mongoose.connect('mongodb://localhost/expressappdb', { useMongoClient: true })  
    .then(() => console.log('MongoDB connected'))
    .catch(err => console.error(err));

//创建可以持久化到数据库的Cat模型
const Cat = mongoose.model('Cat', {name: String});

注意它和Hapi的相似之处

现在更新我们的expressapp/view /cats。带有表单代码的hbs模板

<!-- expressapp/views/cats.hbs -->

...

<form class="" action="/cats" method="post">  
    <input type="text" name="name" placeholder="New cat">
</form>

为了使Express能够解析表单内容,我们需要导入body-parser中间件。在index.js中导入Express行之后,导入body-parser,如下:

// expressapp/index.js

...

const express = require('express');  
const bodyParser = require('body-parser')

另外,在创建我们的Express app行后激活中间件,如下图所示:

// expressapp/index.js

...

// 创建 app
const app = express();

// 解析 application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));  

现在更新我们的“GET”路径 /cats,并创建一个新的“POST”路径,在数据库中创建猫:

// expressapp/index.js

...

// 在/cats路径上显示 cats 
app.get('/cats', function (req, res) {

    let cats = Cat.find((err, cats) => {
        console.log(cats);
        res.render('cats', {cats: cats});
    });
});

// 发布新猫并将其保存到数据库中
app.post('/cats', function (req, res) {  
    console.log(req.body.name);

    let catname = req.body.name;
    let newCat = new Cat({name: catname});

    newCat.save((err, cat) => {
        if (err) {
            console.error(err);
        }
        console.log(`Cat ${req.body.name} saved to database.`)
        res.redirect('/cats');
    });
});

现在,重启服务器并访问localhost:3000/cats。就像Hapi应用程序一样,我们现在会看到一个顶部有表单的空白区域。我们可以输入猫的名字。视图将刷新,显示保存到MongoDB数据库的猫的列表

在这个例子中,Express的设置比Hapi的等效设置稍微复杂一些。但是,请注意,我们实际上不需要对Hapi或Express做太多的工作来设置数据库连接—这里的大部分工作是用于get和POST路由

 

在Hapi和Express之间进行选择

正如我们所看到的,Hapi和Express有很多共同点。两者都有活跃的用户基础,大型企业团队广泛采用Hapi。然而,Express在采用方面继续领先于大多数其他框架,尤其是对于较小的团队

如果你有一个定义良好的应用程序理念,并且符合编程方法的合理缺省值,那么Hapi是有意义的。Hapi在应用程序安全性等方面采取了一种特殊的方法,它的插件系统没有Express中间件生态系统广泛。如果你是一个大型团队,那么Hapi的约定对于保持代码的可维护性非常有用

另一方面,如果你追求灵活性并自己做出大多数开发决策,Express的效果最好。例如,在Express生态系统中可能有10种不同的中间件来做相同的事情。这取决于你从众多的选择中做出选择。如果你有一个应用程序,你不确定它的确切方向,Express是最好的。无论出现什么意外情况,大型Express生态系统都将提供必要的工具

 

学到更多

你可以访问每个框架各自的官方网站开始你的学习使用:

不管怎样,最重要的是你实际地去尝试它们,通过一些例子,然后自己决定哪个对你来说最好

 

转载请注明出处豆豆网

欢迎分享至:

版权声明:原创文章自由转载-非商用-非衍生-保持署名及文章出处(创意共享3.0许可证
转载说明:转载请注明出处豆豆网
部分文章选自网络(文首、末未标明豆豆网的均来自网络),我们对文中观点保持中立,本站涉及软件下载,仅供参考学习、交流之目的,涉及版权请告知删除,邮箱地址:豆豆网博客


发表评论

邮箱地址不会被公开。 必填项已用*标注