我看hapi

Posted Qunar技术沙龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我看hapi相关的知识,希望对你有一定的参考价值。



你可能不知道hapi,我猜你可能用过 joi 这个模块,它们都是 开发的,joi 是很好用的验证模块,我们有理由相信这个团队的核心项目hapi也是不错的。

2011年8月5日,WalmartLabs的一位成员Eran Hammer提交了Hapi的第一次commit。Hapi原本是Postmile的一部分,并且最开始是基于Express构建的。后来它发展成自己自己的框架,正如Eran在他的博客里面所说的:

Hapi基于这么一个想法:配置优于编码,业务逻辑必须和传输层进行分离…

Hapi最新版本为16.1.0(版本帝,而且尤其喜欢升大版本),拥有4879次commits,并且仍然由Eran Hammer维护。

1、hapi简介

Hapi的发音是happy,一般我都读成h-api,哈哈

一句话

Web and services application framework

(web和服务)应用框架,开篇名义,非常好理解,对于web和service开发都是非常好的框架。一般我们见到的到多是web框架,很少有强调服务的,hapi里的api大概就是起名之初就确定了它的定位。一直以来我都喜欢把hapi和restify放到一起,从名字和框架功能上看,他们对于api服务的支持要好于哪些所谓的web框架。

再看一下更详细的定义

hapi is a simple to use configuration-centric framework with built-in support for input validation, caching, authentication, and other essential facilities for building web and services applications. hapi enables developers to focus on writing reusable application logic in a highly modular and prescriptive approach.

  • configuration-centric是以配置为中心

  • 内置用于构建web和service应用的:验证, 缓存, 鉴权, 和其他基础设施

  • hapi让开发者更加专注编写可重用的应用逻辑

2、优点

  • 高性能 - WalmartLabs的那些人开发Hapi的时候,遵守Benchmark Driven Development,其结果hapi是一个high-performance的框架

  • 安全 - Eran Hammer(Hapi开发者组长)是OAuth (安全授权) 规范的最初作者。 他有关注hapi安全的意识,并review所有Hapi包含的代码。很多的成员是是Hapi的核心贡献者,这意味着非常多关注安全的眼睛在关注代码安全

  • 可扩展性 - 应对高并发还是非常好的

  • Mobile 优化 (轻量级 - 为移动电商而构建)

  • 插件架构 - 扩展/增加 你自己的模块 (良好的生态)

  • 对 DevOps 友好 - 基于配置的开发和强大的stats/logging

  • 内置缓存 (Redis, MongoDB 或 Memcached)

  • 核心代码做了100% 测试/代码覆盖率 - 严格的方法来保证质量

  • 可测试性 - hapi内置端到端测试,因为它包含了模块

  • 核心功能内置,而其他特性以插件的形式展现: 

3、hello world

3.1 准备工作

mkdir myproject
cd myproject
npm init -y
npm install --save hapi
touch app.js

3.2 app.js代码

'use strict';

const Hapi = require('hapi');

const server = new Hapi.Server();
server.connection({ port: 3000, host: 'localhost' });

server.route({
   method: 'GET',
   path: '/',
   handler: function (request, reply) {
       reply('Hello, world!');
   }
});

server.route({
   method: 'GET',
   path: '/{name}',
   handler: function (request, reply) {
       reply('Hello, ' + encodeURIComponent(request.params.name) + '!');
   }
});

server.start((err) => {

   if (err) {
       throw err;
   }
   console.log(`Server running at: ${server.info.uri}`);
});

reply和koa里的ctx.body类似,会根据类型进行判断,比如是object类型就会返回json,还是比较方便的。

3.3 过程

  • connection

  • route

  • start

还是比较好理解的,尤其是路由,显式声明,跟express类似。在具名路由写法上,稍有不同, express里是/:name,而hapi里是/{name},大同小异。

3.4 执行

node app
Server running at: http://localhost:3000

总结,表象很简单,明显比express坚决,但当系统复杂到一定程度的时候,其实是一样的,hapi无非将这些实现进行插件化,延时到你用的时候再学,lazy到你看着美好,但实际是类似的。具有一定的迷惑性。换另一个角度看,作为主打api的框架,不就该这样设计么?想复杂你就复杂去,想简单弄个微服务默认就很简单,是很多人心中比较理想的框架。

4、依赖

  "dependencies": {
   "accept": "2.x.x",
   "ammo":  "2.x.x",
   "boom": "4.x.x",
   "call": "4.x.x",
   "catbox": "7.x.x",
   "catbox-memory": "2.x.x",
   "cryptiles": "3.x.x",
   "heavy": "4.x.x",
   "hoek": "4.x.x",
   "iron": "4.x.x",
   "items": "2.x.x",
   "joi": "10.x.x",
   "mimos": "3.x.x",
   "podium": "^1.2.x",
   "shot": "3.x.x",
   "statehood": "5.x.x",
   "subtext": "^4.3.x",
   "topo": "2.x.x"
 },
 "devDependencies": {
   "code": "4.x.x",
   "handlebars": "4.x.x",
   "inert": "4.x.x",
   "lab": "11.x.x",
   "vision": "4.x.x",
   "wreck": "10.x.x"
 },

  • accept: HTTP Accept-* headers parsing.

另外一个类似的express和koa用的是https://github.com/jshttp/accepts,Higher-level content negotiation,所以二者类似的东西

只想说accept的代码太丑了。。。

  • ammo

是HTTP Range 处理工具

相当于express里的https://github.com/jshttp/range-parser

  • boom

HTTP友好的 error 对象

举个例子

Boom.notFound('missing');

Generates the following response payload:

{
   "statusCode": 404,
   "error": "Not Found",
   "message": "missing"
}

感觉就是个体力活

  • call

HTTP 路由。

const router = new Call.Router();
router.add({ method: 'get', path: '/a{b?}c{d}' }, '/a{b?}c{d}');

简单点说就是所谓的配置中心化里对路由的配置。所谓的正则看起来也怪怪的,跟express完全不一样,原因是没有path-to-regexp作为依赖。凑合用吧

  • catbox

多策略对象缓存服务,支持memory cache, Redis, MongoDB, Memcached, Riak, Amazon S3, RethinkDB, Couchbase, Aerospike 和 LevelDB。基本上就是xxStore。

  • catbox-memory

Memory adapter for catbox,即默认缓存实现为内存。

  • cryptiles

通用目的crypto工具,简单点说就是crypto和boom的封装。仅提供3个方法而已

  • heavy

衡量进程负载的模块,统计服务器信息用的。

  • hoek

Node utilities shared amongst the extended hapi universe

提供各种辅助方法,无依赖,类似于lodash或underscore,都是比较实用的方法。但看起来当堆叠而成的,毫无设计感。

如果当新手入门练习还是很不错的。

  • iron

对json对象加解密(MAC)的工具模块

  • items

精简版的实现,提供serial,parallel等方法,有点蛋疼

  • joi

Object schema validation,大概是hapi里最出名的模块的,对于express等项目也是通用的。简单易用。

  • mimos

Mime database interface,是对mime-db的封装

  • podium

Node (semi) compatible event emitter with extra features.是对Node.js默认的事件发射器不满意,想自己写一个功能更强大的,很多人都做过这样的事儿,据说是因为Node.js为了保证效率,不肯增加额外功能。

  • shot

Injects a fake HTTP request/response into a node HTTP server for simulating server logic, writing tests, or debugging.

简单点说,就是为e2e测试提供的工具,fake是伪造的意思,大家就应该知道它的意图了吧!

  • statehood

HTTP 状态管理工具,对header、cookie,加解密,格式化等提供方法

  • subtext

HTTP payload parser,跟express里的body-parser类似,但功能上更多一些

  • topo

带分组排序的工具模块

  • code

BDD风格的断言库,expect式的,不明白为啥其这个名字,八竿子打不着啊。。。。

  • inert

和https://github.com/expressjs/serve-static类似,但集成了更多,比如etag、compress等

吐槽一下,这命名。。。。

  • vision 视觉

Templates rendering support for hapi.js

和https://github.com/tj/consolidate.js类似,都是模板渲染相关的,不明白为啥发明个轮子。。。大概是为了统一hoek处理错误?

  • wreck

HTTP Client Utilities,简单说就是http客户端。类似request模块

总结一下

  • 命名比较难受,类似code、inert这种,让人无比绝望

  • 代码比较难受,完全没有doc,设计感缺乏

  • 很多轮子,大概是历史原因,比如2011年很多库不是那么健全,索性就自己造了。

  • 没有复用社区已有内容

5、插件

代码上并无太多可借鉴性,那就看看hapi比较强大的插件吧。从官方文档上看,共分12类,授权、鉴权、文档、国际化、日志、安全、session、模板、工具、验证、hapi自用的扩展。看上去还是相当不错的,我们试着通过某一类来管中窥豹,看看它的插件是否真的足够强大、丰富。

  • 授权

有2个插件,分别rbac和acl,如果大家做过权限管理,应该对这2个概念不陌生。

acl是最早也是最基本的一种访问控制机制,它的原理非常简单:每一项资源,都配有一个列表,这个列表记录的就是哪些用户可以对这项资源执行CRUD中的那些操作。当系统试图访问这项资源时,会首先检查这个列表中是否有关于当前用户的访问权限,从而确定当前用户可否执行相应的操作。

rbac是把用户按角色进行归类,通过用户的角色来确定用户能否针对某项资源进行某项操作。rbac相对于acl最大的优势就是它简化了用户与权限的管理,通过对用户进行分类,使得角色与权限关联起来,而用户与权限变成了间接关联。rbac模型使得访问控制,特别是对用户的授权管理变得非常简单和易于维护,因此有广泛的应用。

如果做一些后台管理的系统,acl或rbac至少要选一个,可以说,hapi可以开箱即用,可以省去我们许多麻烦。express里虽然也有类似的功能,但貌似都是特别强,大部分都是自己写的。

  • 鉴权

先说我比较熟悉的

  • hapi-auth-basic & hapi-auth-cookie 常见

  • hapi-auth-jwt & hapi-auth-jwt2 说一下jwt,是实现成本最小的token授权机制,应用还是比较广的

  • hapi-passport-saml 看名字就知道,这是对著名的passport模块的扩展,passport主要提供的是对oauth授权的支持,支持google、github、twitter、facebook、OpenID、BrowserID、html form等各种授权,是非常强大模块

  • hapi-session-mongo 使用mongodb存储session,并集成auth,以前为了偷懒,确实这样做过。

还有一些不太熟悉的

  • bell 和passport模块做的事儿类似

  • hapi-auth-anonymous 针对移动做的,匿名用户策略,有点意思

  • hapi-tiny-auth 必须提供用户名和密码才能查看api,弱弱的

鉴权分类下面总计16个插件,整体上来看是非常丰富的,集成了我们日常应用的绝大部分功能。想想hapi从2011到现在将近6年的时间,提供这么多插件,也是可圈可点的。

6、Boilerplates

hapi有很多Boilerplate,既然它主要提供的是api service,那么我们就挑一个api相关的Boilerplate,看看它的最佳实践:

6.1 目录结构

.
├── api/
|   ├── handlers/
|   |   └── home.js   * Sample handler
|   └── index.js      * REST routes
├── config/
|   ├── manifest.js   * Server configuration
|   └── secret.js     * Secret key
├── test/
|   └── api.js        * API test
├── server.js         * Server definition (uses the Glue plugin to read a manifest)
├── auth.js           * Auth strategies
└── package.json

以server.js作为入口,配合auth.js的授权策略,将具体的api丢到api目录,结构还是相当清晰的。config和test遵循了大部分框架的配置,比较有意思的config/secret.js,Secret key在部署的时候需要配置的,很多框架是没有做这个的。

从这个目录看,比较适合微服务类的小而美的api服务。

6.2 Plugins

  • glue - Server composer for hapi.js. 

  • hapi-auth-jwt2 - Secure Hapi.js authentication plugin using JSON Web Tokens (JWT) in Headers, Query or Cookies.

  • blipp - Simple hapi plugin to display the routes table at startup. 

  • good - Hapi process monitor. It listens for events emitted by Hapi Server instances and allows custom reporters to be registered that output subscribed events. 

  • good-console - Console reporting for Good process monitor. 

  • good-squeeze - Simple transform stream for event filtering with good. 

  • lab - Node test utility. 

  • code - BDD assertion library. 

  • nodemon - Monitor for any changes in your node.js application and automatically restart the server. 

  • eslint - A fully pluggable tool for identifying and reporting on patterns in javascript

  • eslint-plugin-import - ESLint plugin with rules that help validate proper imports. 

  • npm-run-all - A CLI tool to run multiple npm-scripts in parallel or sequential. 

6.3 归纳

  • glue是一个按照配置进行装配的工具,hapi的以配置为中心,也有glue的一分功劳

  • hapi-auth-jwt2 用于鉴权

  • blipp是一个打印路由列表的模块,如果大家熟悉rails,一定知道rake routes

  • good系列是进程监控用的,也是插件机制,支持console、squeeze等。

  • 测试部分,采用的hapi默认的lab + code

  • elint,nodemon,npm-run-all都是比较中规中矩的

7、总结

7.1 hapi好的点

  • 定位非常准,以api作为切入点,区别express等传统web框架

  • 背后有沃尔玛公司应用、支持,项目也还是比较健康的,是经过大规模应用的

  • 基本可以做到开箱即用,和thinkjs差不多,但比thinkjs的插件要丰富很多

  • 文档还算可以

  • 辅助性的工具还是非常完善的,尤其是devops相关的,比express等要好很多

7.2 不好的点

  • 框架的api和代码具有沃尔玛风格,不是很规范

  • 对于新特性没有内置,也还是算比较传统

hapi里处处是配置,处处插件化,毁誉参半吧,对它的view部分设计不是很喜欢,有兴趣的可以以测测性能,应该不会比express好。

如果说作为api类的服务,hapi是一个不错的选择


以上是关于我看hapi的主要内容,如果未能解决你的问题,请参考以下文章

@hapi/lab 与 lab-transform-typescript 正在返回错误的代码覆盖率结果

ionic 后台Api服务, 使用rest-hapi , node.js 创建 RESTful API Service , 附完整源代码

“502 Bad Gateway”将 hapi.js 部署到 AWS Beanstalk?

Hapi.js —— Node.js 服务器端应用开发框架

基于hapi的Node.js后端开发

无效的寄存器选项“值”必须是一个对象 hapi-auth-jwt2