最近一直在学习hapiJs,有点koa2的基础以为会容易呢,但是全英文的API,不同于koa2的实现方式,看起来大写的懵啊,整理此文,希望能够帮助到一些想要入门hapi的新人。
1、搭建项目
1.1 首先我们创建一个目录‘hapiDemo’作为项目目录,输入命令:
makdir hapiDemo。
1.2 打开项目目录,初始化,命令如下:
cd hapiDemo,
初始化:npm init,
1.3 项目基础配置:
安装hapi: npm install --save hapi;
项目使用了ES6语法安装babel: npm intall --save hapi;
npm start默认是的启动文件是sever.js,本项目是index.js,修改启动命令,
下文中还是用了一些插件,请自行安装。
1.4 初始化并安装hapi是第一步,hapiDemo 项目的基础项目结构如下图:
这些文件的作用是:
config:配置文件目录,db_config:数据库的配置信息,plugin_config:注册插件的相关信息
controllers:controllers下是业务逻辑
models:model 层
public:静态文件
routes:路由信息
log:日志信息
index.js:项目启动入口文件
server.js:服务配置信息
2、hapi start
2.1 创建启动服务配置文件server.js,输入一下代码:
const Hapi = require('hapi');
//Create a hapi server
var server = new Hapi.Server();
/**************server config********/
let connectionOptions={
port: 3333,
host: 'localhost'
};
server.connection(connectionOptions);
// Export the server to be required elsewhere.
module.exports = server;
2.2 配置完服务文件,现在我们来启动服务,先新建一个index.js文件:
const server=require('./server');
server.start(err=>{
if(err) throw err;
console.log( `Server running at: ${server.info.uri}`);
});
输入node启动命令:npm start
将会显示:Server running at: http://localhost:3333。OK,项目启动成功。
我们在浏览器中输入url:http://localhost:3333,
找不到路由。
3 处理路由
3.1 将路由文件放在routes文件夹里,我们将创建多个路由,分模块创建,首先修改server.js文件,新增如下代码:
const route=require('./routes');
// Add the server routes
route.forEach(function(api){
server.route(api);
});
在routes新建index.js,每新增一个路由文件,在index.js文件中引入。
module.exports=[
require(__dirname+'/helloWorld.js'),
require(__dirname+'/login.js'),
require(__dirname+'/file.js'),
require(__dirname+'/auth.js')
]
3.2 定义路由需要三个基础元素:path,method,handler。Methods的选项可以是任何有效的HTTP方法,也可以是方法数组。path选项必须是字符串,尽管它可以包含命名参数。若要在路径中命名参数,只需用{ }将其包装。handler选项是一个函数,它接受两个参数:request和request。
在routes里新建一个helloWorld.js:
let index={
method: 'GET',
path: '/',
handler: function(request, reply){
reply('hello,world');
}
};
let hello={
method: ['GET', 'POST'],
path: '/hello/{user?}',
handler: function (request, reply) {
reply('Hello ' + encodeURIComponent(request.params.user) + '!');
}
};
module.exports=[index,hello];
保存重启服务,在浏览器中访问,显示如下:
更多用法请查看api:https://hapijs.com/api#route-...
4 加载插件
一般在nodeJS中,我们加载一个插件,安装后使用require 插件名称就OK了,但是在hapiJS中,需要通过server.register()方法引入。
以下文中使用处理静态文件的插件 inert 举例:
server.register(require('inert'), (err) => {
if (err) {
console.error('Failed to load a plugin:', err);
}
});
但不是所有的插件都需要使用server.register()引入,直接使用require即可。
为什么使用server.register()引用,我至今不是很清楚。
在本项目中,我把所有的插架配置放在了config/plugin_config.js中:
const SwaggerOptions = {
info: {
'title': 'hapiDemo API Documentation',
'version': '0.0.1'
}
};
const goodOptions = {
ops: {
interval: 1000
},
reporters: {
myConsoleReporter: [{
module: 'good-squeeze',
name: 'Squeeze',
args: [{ log: '*', response: '*' }]
}, {
module: 'good-console'
}, 'stdout'],
myFileReporter: [{
module: 'good-squeeze',
name: 'Squeeze',
args: [{ log: '*', response: '*' ,request:'*'}]
}, {
module: 'good-squeeze',
name: 'SafeJson'
}, {
module: 'good-file',
args: ['./log/fixtures/awesome_log']
}],
myHTTPReporter: [{
module: 'good-squeeze',
name: 'Squeeze',
args: [{ error: '*' }]
}, {
module: 'good-http',
args: ['http://prod.logs:3000', {
wreck: {
headers: { 'x-api-key': 12345 }
}
}]
}]
}
};
module.exports = [
{
register:require('good'),
goodOptions,
},
{
register:require('hapi-auth-jwt2')
},
{
register:require('inert')
},
{
register:require('hapi-auth-basic')
},
{
register:require('./../auth')
},
{
'register': require('hapi-swagger'),
'options': SwaggerOptions
},
{
register:require('vision')
}
];
在server.js中:
const plugins=require('./config/plugin_config');
//Register all plugins
server.register(plugins, function (err) {
if (err) {
throw err; // something bad happened loading a plugin
}
});
5 渲染静态文件和视图
5.1 在Web应用程序中,不可避免地,需要提供一个简单的文件,图片或者静态html。在hapi 中使用 inert 插件来处理静态文件。
npm install --save inert
在routes文件夹中创建一个file.js:
let static={
method: 'GET',
path: '/staticFile',
handler: function (request, reply) {
reply.file('./public/static.html');
}
};
module.exports=static;
在public文件夹下新增一个static.html的文件,内容随意。保存然后运行。
5.2 hapi 可以使用模板渲染,hapi默认使用的是handlebars,要开始视图,首先我们必须在服务器上配置至少一个模板引擎。这是通过使用server.views方法做的,修改server.js文件:
server.register(plugins, function (err) {
server.views({
engines: {
'html': {
module: require('handlebars')
}
},
relativeTo:__dirname,
path:'public/templates'
});
if (err) {
throw err; // something bad happened loading a plugin
}
});
加载 vision 插件,它增加了模板渲染支持哈啤。
更多配置项:https://hapijs.com/tutorials/...
渲染势图,在file.js文件中新增路由:
let temp={
method: 'GET',
path: '/view',
config: {
auth: false,
handler: function (request, reply) {
reply.view('login');
}
}
};
module.exports=[static,temp];
login的内容自行填充
6 访问数据库
在web应用程序中,我们可能写特别多数据库访问层的代码,数据库保存,删除,读取,那hapi如何访问数据库呢?本demo以MySQL为例。
不懂数据库的程序员不是好程序员,但是我早早就把数据库的一点皮毛还给了老师,我选择Node的ORM框架Sequelize来操作数据库。
hapi-sequelize插件对sequelize做了很简单的封装,但是它对Hapi和sequelize的版本有要求,在本项目中没有使用,有兴趣的的可以研究 https://github.com/danecando/...
6.1 在server.js添加代码:
const models=require('./models');
//Connect database
var initDb = function(){
var sequelize = models.sequelize;
//Determine if the database connection is successful
sequelize.sync({force: false}).then(function() {
console.log("connection successed");
}).catch(function(err){
console.log("connection failed due to error: %s", err);
});
};
initDb();
6.2 使用Sequelize操作MySQL需要先做两件准备工作,
(1)创建一个sequelize对象实例,连接数据库,在models新增index.js,代码如下:
const fs = require("fs");
const path = require("path");
const Sequelize = require("sequelize");
const config = require('../config/db_config');
let db = {};
//创建一个sequelize对象实例,连接数据库
let sequelize = new Sequelize(config.database, config.username, config.password,{
host: config.host,
dialect: 'mysql',
pool: {
max: 5,
min: 0,
idle: 30000
}
});
fs
.readdirSync(__dirname)
.filter(function(file) {
return (file.indexOf(".") !== 0) && (file !== "index.js");
})
.forEach(function(file) {
var model = sequelize["import"](path.join(__dirname, file));
db[model.name] = model;
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
db_config文件是数据库的配置信息。
(2)定义模型文件user(在本项目中主要是实现登陆),告诉Sequelize如何映射数据库表。
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("User", {
id:{
type: DataTypes.INTEGER,
primaryKey: true
},
user_no:DataTypes.STRING,
old_kn_userid:DataTypes.STRING,
nickname:DataTypes.STRING,
password:DataTypes.STRING,
}, {
freezeTableName: true, // Model 对应的表名将与model名相同
timestamps: false
});
return User;
};
更多Sequelize的学习可以参考:https://itbilu.com/nodejs/npm...
6.3 经过配置后,接下来我们可以在路由handler中使用这个实例啦。
新建一个login.js:
const Joi=require('joi');
const controllers=require('../controllers');
let login2={
method: 'get',
path: '/tologin2',
config: {
validate:{
query: {
nickname:Joi.min(6).max(30)required(),//校验
}
},
id: 'login2'
},
handler: controllers.user.login2,
};
module.exports=login2;
joi 是 hapijs 自带的数据校验模块,他已经高度封装常用的校验功能,更多用法:https://github.com/hapijs/joi...。
6.4 接下来我们就要访问数据库啦。
(1)新建index.js
const requireDirectory = require('require-directory');
module.exports = requireDirectory(module);
require-directory的作用是递归遍历指定目录,require()每个文件,并返回一个包含这些模块嵌套的hash结构。
(2)user.js:
let models=require('../models')
module.exports={
login2:function(request,reply){
let userInfo=models.User.findOne({
where:{
nickname: request.query.nickname
}
}).then(function(result){
let reponseMess={};
if(result!==null){
reponseMess={
code:1,
message:'已经在数据库中查询到'
}
}else{
reponseMess={
code:-1,
message:'未已经在数据库中查询到'
}
}
reply(reponseMess);
});
}
};
简单的demo查询,用户是否已存在
7 自动生成swagger文档
使用hapi写api时,有种代码既文档的感觉,而且这些代码也真的可以自动生成swagger文档。
使用hapi插件hapi-swagger,简单配置下插件,先修改下plugin_config.js文件:
const SwaggerOptions = {
info: {
'title': 'hapiDemo API Documentation',
'version': '0.0.1'
}
};
module.exports = [
{
'register': require('hapi-swagger'),
'options': SwaggerOptions
},
];
然后修改/tologin2:
let login2={
method: 'get',
path: '/tologin2',
config: {
auth:false,
description: 'Routing with parameters',
notes: 'login api',
tags: ['api'],//写上这句,开启生成swagger功能
validate:{
query: {
nickname:Joi.required(),
}
},
id: 'login2'
},
handler: controllers.user.login2,
};
运行,打开 http://localhost:3333/documen...
8 测试
未完待续……
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。