搜了國內的文章 使用 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…