搜了国内的文章 使用 Hapi
开发 RESTful API
的很少很少,一些 社区介绍的部分在新版本中已经不在适用,新版本中有很多变化
什么是 RESTful API
RESTful API: REST -- REpresentational State Transfer,英语的直译就是“表现层状态转移”
可以参考下面理解:
-
理解本身的 REST 架构风格:www.infoq.com/cn/articles…
-
理解 RESTful 架构:www.ruanyifeng.com/blog/2011/0…
-
Restful API 设计指南:www.ruanyifeng.com/blog/2014/0…
Hapi
Hapi 是一个 Node.js 的 web 框架,即一个构建应用程序和服务丰富的框架。 目前 Hapi v17 仅支持 node v8.9.0 及以上版本
Hapi 特性
- 认证和授权:内置的验证和授权方案
- 缓存:提供客户端和服务端缓存,catbox
- 路由: 内置路由
- 验证:使用 Joi
- Cookies:提供了配置选项处理 cookie
- 日志: 内置的日志记录
- 错误处理:boom
- 进程监控:hapi 插件 good
使用 Hapi 的必要条件
- 安装 Node.js v8.9.0 及以上
- 安装 MongoDB
- 熟悉数据库概念和 JavaScript 的应用知识
Hapi v17 新的变化
-
server.connection 方法移除,现在使用
const server = new Hapi.Server({ host: 'localhost', port: 3000 }) 复制代码
-
开启关闭服务方法 完全异步,没有回调
try { await server.start() } catch (err) { console.log(err) } try { await server.stop() } catch (err) { console.log(err) } 复制代码
-
reply() 回调方法移除,response.hold() 和 response.resume() 方法不在可用
// 以前 const handler = function (request, reply) { return reply('ok'); }; // 现在 const handler = function (request, h) { return 'ok'; }; 复制代码
更多使用 h 的示例如下:
const handler = (request, h) => { // return a string return 'ok' // return an object and hapi creates JSON out of it return { name: 'Authentication Library', library: true } // redirect to 404 return h.redirect('/404') // return a view return h.view('index', { name: 'Authentication Library' }) // use the "h" response toolkit to create a response return h .response(thisHTML) .type('text/html') .header('X-Custom', 'my-value') .code(201) } 复制代码
-
三个请求事件类型 request, request-interval, request-error 合并成一个单一的 request 事件
-
触发的方法 像 server.on, request.on, response.on 被替换,使用server.events.on(), request.events.on(), response.events.on() 替代
-
新的请求扩展:
server.ext('onPreAuth', (request, h) => { … }) server.ext('onCredentials', (request, h) => { … }) server.ext('onPostAuth', (request, h) => { … }) ] 复制代码
-
在路由定义的时候 替换 config 为 options
server.route({ method: 'POST', path: '/', options: { … } }) 复制代码
-
插件 目前使用
exports.plugin = { register, name, version, multiple, dependencies, once, pkg } 复制代码
更多变化详见GitHub 完整的更新日志
正题:使用 Hapi 构建一个简单的 api
在这个示例中提供 CURD 操作
- Get all demos - GET /demos
- Get one demo - GET /demo/11
- Create a demo - POST /demos
- Edit a demo - PUT /demos/11
- Delete a demo - DELETE /demos/11
A demo 有下面的属性:
- name
- age
1 创建项目目录 demo,最后结构如下:
demo
├── package.json
├── server.js
├── .gitignore (可选)
└── src
├
├── controller
| └── test.js
└─── models
└── test.js
复制代码
2 编写 model
src/models/test.js
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const demoModel = new Schema({
name: { type: String, required: true, index: { unique: true } },
age: { type: Number, required: true }
});
module.exports = mongoose.model('Demo', demoModel, 'demos');
复制代码
编写 controllers
src/controllers/test.js
var Demo = require('../models/test');
/**
* List Demos
*/
exports.list = (req, h) => {
return Demo.find({}).exec().then((demo) => {
return { demos: demo };
}).catch((err) => {
return { err: err };
});
}
/**
* Get Demo by ID
*/
exports.get = (req, h) => {
return Demo.findById(req.params.id).exec().then((demo) => {
if(!demo) return { message: 'Demo not Found' };
return { demos: demo };
}).catch((err) => {
return { err: err };
});
}
/**
* POST a Demo
*/
exports.create = (req, h) => {
const demoData = {
name: req.payload.name,
age: req.payload.age
};
return Demo.create(demoData).then((demo) => {
return { message: "Demo created successfully", demo: demo };
}).catch((err) => {
return { err: err };
});
}
/**
* PUT | Update Demo by ID
*/
exports.update = (req, h) => {
return Demo.findById(req.params.id).exec().then((demo) => {
if (!demo) return { err: 'Demo not found' };
demo.name = req.payload.name;
demo.breed = req.payload.breed;
demo.age = req.payload.age;
demo.image = req.payload.image;
demo.save(dogData);
}).then((data) => {
return { message: "Demo data updated successfully" };
}).catch((err) => {
return { err: err };
});
}
/**
* Delete Demo by ID
*/
exports.remove = (req, h) => {
return Demo.findById(req.params.id).exec(function (err, demo) {
if (err) return { dberror: err };
if (!demo) return { message: 'Demo not found' };
demo.remove(function (err) {
if (err) return { dberror: err };
return { success: true };
});
});
}
复制代码
编写 server.js
'use strict';
const Hapi = require('hapi');
const mongoose = require('mongoose');
const DemoController = require('./src/controllers/test');
const MongoDBUrl = 'mongodb://localhost:27017/demoapi';
const server = new Hapi.Server({
port: 3000,
host: 'localhost'
});
server.route({
method: 'GET',
path: '/demos',
handler: DemoController.list
});
server.route({
method: 'GET',
path: '/demos/{id}',
handler: DemoController.get
});
server.route({
method: 'POST',
path: '/demos',
handler: DemoController.create
});
server.route({
method: 'PUT',
path: '/demos/{id}',
handler: DemoController.update
});
server.route({
method: 'DELETE',
path: '/demos/{id}',
handler: DemoController.remove
});
(async () => {
try {
await server.start();
// Once started, connect to Mongo through Mongoose
mongoose.connect(MongoDBUrl, {}).then(() => { console.log(`Connected to Mongo server`) }, err => { console.log(err) });
console.log(`Server running at: ${server.info.uri}`);
}
catch (err) {
console.log(err)
}
})();
复制代码
测试 API
使用 postman 测试 http://localhost:3000/demos
增加授权验证
目前步骤有点多,还需要注册一个平台,暂不做详细说明了
参考
- GitHub:github.com/hapijs/hapi
- website: hapijs.com/
- issue:github.com/hapijs/disc…
- developing-restful-apis-with-hapi v17
- github.com/hapijs/hapi…