从Web应用程序设计和开发的角度来看,Express和Hapi如何相互比较?对于基本示例,它们看似相似,但我有兴趣了解有关整体应用程序结构的主要差异。
例如,据我所知,Hapi使用different路由机制,该机制不考虑注册顺序,可以执行更快的查找,但与Express相比是有限的。还有其他重要的区别吗?
还有article关于选择Hapi(快速通过)开发新的npmjs.com网站,本文指出" Hapi的插件系统意味着我们可以隔离不同的方面和服务应用程序将允许未来的微服务。另一方面,Express需要更多的配置来获得相同的功能"它究竟意味着什么?
答案 0 :(得分:214)
这是一个很大的问题,需要很长的答案才能完成,所以我只是解决最重要差异的一部分。抱歉,它仍然是一个冗长的答案。
当你说:
时,你是绝对正确的对于基本示例,它们看似相似
两个框架都在解决相同的基本问题:提供一个方便的API来在节点中构建HTTP服务器。也就是说,比单独使用低级本机http
模块更方便。 http
模块可以完成我们想要的任何事情,但编写应用程序非常繁琐。
为实现这一目标,他们都使用了很长时间以来在高级Web框架中存在的概念:路由,处理程序,插件,身份验证模块。它们可能并不总是具有相同的名称,但它们大致相同。
大多数基本示例都是这样的:
快递:
app.get('/', function (req, res) {
getSomeValue(function (obj) {
res.json({an: 'object'});
});
});
HAPI:
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
getSomeValue(function (obj) {
reply(obj);
});
}
});
这里的差异并不完全是开创性的吗?那么为什么选择一个呢?
简单的答案是hapi更多,它开箱即用的功能更多。当你只看上面的简单例子时,这可能不太清楚。事实上,这是故意的。简单的案例很简单。因此,让我们研究一些重大差异:
Express旨在非常小。通过在http
之上为您提供一个只有轻薄粉尘的小型API,您在添加其他功能方面仍然非常自负。如果要读取传入请求的正文(非常常见的任务),则需要安装separate module。如果您期望将各种内容类型发送到该路由,您还需要检查Content-type
标头以检查它是什么并相应地进行解析(form-data vs JSON vs multi-part for例如),经常使用单独的模块。
hapi具有丰富的功能集,通常通过配置选项公开,而不需要编写代码。例如,如果我们想确保在运行处理程序之前将请求主体(有效负载)完全读入内存并进行适当的解析(自动基于内容类型),那么它只是一个简单的option :
server.route({
config: {
payload: {
output: 'data',
parse: true
}
},
method: 'GET',
path: '/',
handler: function (request, reply) {
reply(request.payload);
}
});
您只需要比较两个项目的API文档,看看hapi提供了更大的功能集。
hapi包含Express内置的一些内置功能(据我所知):
hapi拥有request lifecycle并提供extension points,它们与中间件功能相当,但在请求生命周期中存在多个已定义的点。
Walmart构建hapi并停止使用Express的原因之一是将Express应用程序拆分为单独的部分并让不同的团队成员安全地工作在他们的块上是一件令人沮丧的事情。出于这个原因,他们在hapi中创建了plugin system。
插件就像一个子应用程序,您可以在hapi应用程序中执行所有操作,添加路由,扩展点等。在插件中,您可以确保不会破坏应用程序的其他部分,因为路线的注册顺序并不重要,您不能创建冲突的路线。然后,您可以将此插件组合到服务器中并进行部署。
因为Express让您开箱即用,所以当您需要向项目中添加任何内容时,您需要向外看。在使用hapi时,很多时候,您需要的功能是内置的,或者核心团队创建的模块。
最小的声音很棒。但如果您正在构建一个严肃的制作应用程序,那么您最终可能需要所有这些东西。
自己评估他们。考虑一下您的需求以及两者中的哪一个解决了您最关心的问题。在两个社区(IRC,Gitter,Github)畅游,看看你喜欢哪个。不要接受我的话。而且快乐的黑客!
免责声明:我作为book on hapi的作者有偏见,以上内容主要是我个人的意见。
答案 1 :(得分:49)
我的组织正在与Hapi合作。这就是我们喜欢它的原因。
哈皮是:
如果你想直接听Eran Hammer(Hapi的领导)
在过去四年中,hapi逐渐成为许多项目的首选框架,无论大小。 hapi的独特之处在于它能够扩展到大型部署和大型团队。随着项目的发展,其复杂性也在增加 - 工程复杂性和流程复杂性。 hapi的架构和哲学处理增加的复杂性,而无需不断重构代码[read more]
Hapi入门并不像ExpressJs那么容易,因为Hapi没有相同的“明星力量”......但是一旦你感到舒服,你就会获得很多里程。作为一个不负责任地使用ExpressJs几年的新黑客,我花了大约2个月的时间。如果您是经验丰富的后端开发人员,您将知道如何阅读文档,您可能甚至都不会注意到这一点。
Hapi文档可以改进的领域:
我认为身份验证将是最具挑战性的部分,因为您必须决定使用什么样的身份验证策略(基本身份验证,Cookie,JWT令牌,OAuth)。虽然技术上不是Hapi的问题,会话/认证环境如此分散......但我确实希望他们为此提供了一些手握。这将大大提高开发人员的幸福感。
剩下的两个实际上并不那么困难,文档可以写得稍好一些。
答案 2 :(得分:1)
关于Hapi或为何使用Hapi JS的简要事实
Hapi以配置为中心 它在框架中内置了身份验证和授权 它在经过战斗考验的气氛中发布,并确实证明了其价值 所有模块都有100%的测试覆盖率 它注册了远离核心HTTP的最高抽象级别 通过插件架构可以轻松实现指南针
Hapi是更好的性能选择 Hapi使用不同的路由机制,该机制可以进行更快的查找,并考虑注册顺序。 但是,与Express相比,它是相当有限的。而且由于有了Hapi插件系统, 隔离将在将来以多种方式帮助应用程序的不同方面和服务。
用法
与Express相比,Hapi是最优选的框架。 Hapi主要用于大型企业应用程序。
开发人员在创建企业应用程序时不选择Express的几个原因是:
在Express中撰写路线比较困难
大多数情况下,中间件都处于阻碍状态;每次定义路线时,都必须编写尽可能多的代码。
对于希望构建RESTful API的开发人员来说,Hapi是最佳选择。 Hapi具有微服务架构,还可以根据某些参数将控制权从一个处理程序转移到另一个处理程序。使用Hapi插件,您可以享受 围绕HTTP进行更高级别的抽象,因为您可以将业务逻辑划分为易于管理的部分。
Hapi的另一个巨大优势是,当您配置错误时,它会提供详细的错误消息。 Hapi还允许您默认配置文件上传大小。如果最大上传大小受到限制,则可以向用户发送错误消息,告知文件大小太大。这样可以防止您的服务器崩溃,因为文件上传将不再尝试缓冲整个文件。
使用express可以实现的所有目标,也可以使用hapi.js轻松实现。
Hapi.js非常时尚,并且可以很好地组织代码。如果您看到它如何进行路由并将核心逻辑放入控制器中 您一定会喜欢上它。
Hapi.js正式提供了几个专门用于hapi.js的插件,范围从基于令牌的身份验证到会话管理,等等。 这是一个广告。这并不意味着无法使用传统的npm,所有这些都受hapi.js支持
如果您在hapi.js中进行编码,那么代码将非常易于维护。
答案 3 :(得分:0)
还要补充一点,Hapi已开始支持从版本16开始的“ http2”调用(如果我没记错的话)。但是,直到Express 4为止,Express尚未直接支持'http2'模块。尽管他们已经在Express 5的Alpha版本中发布了该功能。
答案 4 :(得分:0)
public static IQueryable<ProductDTO> FilterAgeByGroup(
IQueryable<ProductDTO> query,
List<string> filters)
{
var criteria = filters
.Select(filter => {
Items.TryGetValue(filter, out var criterion);
return criterion; // if filter is not in Items.Keys, code will be 0
})
.Where(criterion => criterion.code > 0) // excludes filters not matched in Items
.ToList();
if (!criteria.Any()) { return query; }
var type = typeof(ProductDTO);
var x = Parameter(t);
// creates an expression that represents the number of years old this ProductDTO is:
// 2019 - x.YearManufactured
var yearsOldExpr = Subtract(
Constant(DateTime.UtcNow.Year),
Property(x, t.GetProperty("YearManufactured"))
);
var filterExpressions = new List<Expression>();
foreach (var criterion in criteria) {
Expression minExpr = null;
if (criterion.min != null) {
// has to be at least criteria.min years old; eqivalent of:
// 2019 - x.YearManufactured >= 10
minExpr = GreaterThanOrEqual(
yearsOldExpr,
Constant(criterion.min)
);
}
Expression maxExpr = null;
if (criterion.max != null) {
// has to be at least criteria.max years old; equivalent of
// 2019 - x.YearManufactured <= 20
maxExpr = LessThanOrEqual(
yearsOldExpr,
Constant(criterion.max)
)
}
if (minExpr != null && maxExpr != null) {
filterExpressions.Add(AndAlso(minExpr, maxExpr));
} else {
filterExpressions.Add(minExpr ?? maxExpr); // They can't both be null; we've already excluded that possibility above
}
}
Expression body = filterExpressions(0);
foreach (var filterExpression in filterExpressions.Skip(1)) {
body = OrElse(body, filterExpression);
}
return query.Where(
Lambda<Func<ProductDTO, bool>>(body, x)
);
}
答案 5 :(得分:0)
我最近开始使用Hapi,对此我感到非常满意。我的原因是
更易于测试。例如:
server.inject
可让您运行该应用并获得响应,而无需运行和收听。server.info
给出当前的uri,端口等。server.settings
访问配置,例如server.settings.cache
获取当前的缓存提供程序/test
文件夹,以查看有关如何模拟/测试/存根等的建议。开箱即用file uploads,从端点等返回流。
基本插件与核心库一起维护。例如template parsing,caching等。附加的好处是相同的编码标准适用于所有基本事物。
健全的错误和错误处理。 Hapi validates config options并保留一个内部路由表以防止重复路由。这在学习时非常有用,因为可以及早抛出错误,而不是需要调试的意外行为。