教程:Node.js 和 MongoDB JSON REST API 服務器與 Mongoskin 和 Express.js
更新3: 本教程的 Expess 4 版本可在 Express.js 4、Node.js 和 MongoDB REST API 教程以及 github.com/azat-co/rest-api-express(master 分支)上獲得。本教程適用於 Express 3.x。
更新2 :“Mongoskin 刪除了‘db.collection.id’並添加了一些 actionById 方法”從此拉取請求中更改了此代碼。要使用本文中的代碼,只需安裝舊版本的 Mongoskin(0.5.0?)。 GitHub 中的代碼將適用於 Mongoskin 1.3.20。
更新2 :“Mongoskin 刪除了‘db.collection.id’並添加了一些 actionById 方法”從此拉取請求中更改了此代碼。要使用本文中的代碼,只需安裝舊版本的 Mongoskin(0.5.0?)
更新 :使用來自此存儲庫 github.com/azat-co/rest-api-express(express3 分支)的增強代碼。
注意: 本文是 Express.js 指南的一部分:Express.js 綜合書籍。
本教程將引導您使用 Mocha 和 Super Agent 庫編寫測試,然後以測試驅動的開發方式使用它們,利用 Express.js 框架和用於 MongoDB 的 Mongoskin 庫構建一個 Node.js 免費的 JSON REST API 服務器。在這個 REST API 服務器中,我們將執行創建、讀取、更新和刪除 (CRUD) 操作和利用 app.param()
的 Express.js 中間件概念 和 app.use()
方法。
測試覆蓋率
首先讓我們編寫功能測試,向我們即將創建的 REST API 服務器發出 HTTP 請求。如果您知道如何使用 Mocha 或者只是想直接跳轉到 Express.js 應用程序實現,請隨意這樣做。您也可以使用 CURL 終端命令進行測試。
假設我們已經安裝了 Node.js、NPM 和 MongoDB,讓我們創建一個 new 文件夾(或者如果您編寫測試使用該文件夾):
mkdir rest-api
cd rest-api
我們將使用 Mocha、Expect.js 和 Super Agent 庫。要安裝它們,請從項目文件夾運行以下命令:
$ npm install [email protected] --save-dev
$ npm install [email protected] --save-dev
$ npm install [email protected] --save-dev
現在讓我們創建 express.test.js 同一文件夾中的文件將有六個套件:
- 創建一個新對象
- 通過 ID 檢索對象
- 檢索整個集合
- 通過 ID 更新對象
- 通過 ID 檢查更新的對象
- 按 ID 移除對象
使用 Super Agent 的鍊式函數,HTTP 請求變得輕而易舉,我們將把它們放在每個測試套件中。這是 express.test.js 的完整源代碼 文件:
var superagent = require('superagent')
var expect = require('expect.js')
describe('express rest api server', function(){
var id
it('post object', function(done){
superagent.post('http://localhost:3000/collections/test')
.send({ name: 'John'
, email: '[email protected]'
})
.end(function(e,res){
// console.log(res.body)
expect(e).to.eql(null)
expect(res.body.length).to.eql(1)
expect(res.body[0]._id.length).to.eql(24)
id = res.body[0]._id
done()
})
})
it('retrieves an object', function(done){
superagent.get('http://localhost:3000/collections/test/'+id)
.end(function(e, res){
// console.log(res.body)
expect(e).to.eql(null)
expect(typeof res.body).to.eql('object')
expect(res.body._id.length).to.eql(24)
expect(res.body._id).to.eql(id)
done()
})
})
it('retrieves a collection', function(done){
superagent.get('http://localhost:3000/collections/test')
.end(function(e, res){
// console.log(res.body)
expect(e).to.eql(null)
expect(res.body.length).to.be.above(0)
expect(res.body.map(function (item){return item._id})).to.contain(id)
done()
})
})
it('updates an object', function(done){
superagent.put('http://localhost:3000/collections/test/'+id)
.send({name: 'Peter'
, email: '[email protected]'})
.end(function(e, res){
// console.log(res.body)
expect(e).to.eql(null)
expect(typeof res.body).to.eql('object')
expect(res.body.msg).to.eql('success')
done()
})
})
it('checks an updated object', function(done){
superagent.get('http://localhost:3000/collections/test/'+id)
.end(function(e, res){
// console.log(res.body)
expect(e).to.eql(null)
expect(typeof res.body).to.eql('object')
expect(res.body._id.length).to.eql(24)
expect(res.body._id).to.eql(id)
expect(res.body.name).to.eql('Peter')
done()
})
})
it('removes an object', function(done){
superagent.del('http://localhost:3000/collections/test/'+id)
.end(function(e, res){
// console.log(res.body)
expect(e).to.eql(null)
expect(typeof res.body).to.eql('object')
expect(res.body.msg).to.eql('success')
done()
})
})
})
要運行測試,我們可以使用 $ mocha express.test.js
命令。
依賴關係
在本教程中,我們將使用 Mongoskin,這是一個 MongoDB 庫,它是用於 Node.js 的普通舊原生 MongoDB 驅動程序的更好替代品。此外,Mongoskin 比 Mongoose 更輕量級且無模式。如需更多信息,請查看 Mongoskin 比較簡介。
Express.js 是核心 Node.js HTTP 模塊對象的包裝器。 Express.js 框架建立在 Connect 中間件之上,並提供了大量的便利。有些人將框架與 Ruby 的 Sinatra 進行比較,因為它是非主觀的和可配置的。
[旁注]
閱讀博客文章很好,但觀看視頻課程更好,因為它們更具吸引力。
許多開發人員抱怨 Node.js 上缺乏負擔得起的高質量視頻材料。觀看 YouTube 視頻會讓人分心,花 500 美元購買 Node 視頻課程很瘋狂!
去看看 Node University,它有關於 Node 的免費視頻課程:node.university。
[旁注結束]
如果您創建了 rest-api
上一節中的文件夾測試覆蓋率 ,只需運行以下命令即可為應用程序安裝模塊:
npm install [email protected] --save
npm install [email protected] --save
實施
首先,讓我們定義我們的依賴項:
var express = require('express')
, mongoskin = require('mongoskin')
在 3.x 版本之後,Express 簡化了其應用實例的實例化,以這種方式將給我們一個服務器對象:
var app = express()
要從請求正文中提取參數,我們將使用 bodyParser()
看起來更像配置語句的中間件:
app.use(express.bodyParser())
中間件(以這種形式和其他形式)是 Express.js 和 Connect 中用於組織和重用代碼的強大且方便的模式。
與 bodyParser()
一樣 Mongoskin 使我們免於解析 HTTP 請求的主體對象的障礙,可以通過一行代碼輕鬆連接到 MongoDB 數據庫:
var db = mongoskin.db('localhost:27017/test', {safe:true});
注意:如果您希望連接到遠程數據庫,例如 MongoHQ 實例,請將字符串替換為您的用戶名、密碼、主機和端口值。這是 URI 字符串的格式:mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
app.param()
方法是另一個 Express.js 中間件。它基本上說“每次在請求處理程序的 URL 模式中有這個值時做一些事情 ”。在我們的例子中,當請求模式包含一個字符串 collectionName
時,我們選擇一個特定的集合 以冒號為前綴(您將在後面的路線中看到它):
app.param('collectionName', function(req, res, next, collectionName){
req.collection = db.collection(collectionName)
return next()
})
只是為了用戶友好,讓我們在根路由中添加一條消息:
app.get('/', function(req, res) {
res.send('please select a collection, e.g., /collections/messages')
})
現在真正的工作開始了,下面是我們如何檢索按 _id
排序的項目列表 並且限制為 10:
app.get('/collections/:collectionName', function(req, res) {
req.collection.find({},{limit:10, sort: [['_id',-1]]}).toArray(function(e, results){
if (e) return next(e)
res.send(results)
})
})
你有沒有註意到一個 :collectionName
URL 模式參數中的字符串?這個和之前的 app.param()
中間件為我們提供了 req.collection
指向我們數據庫中指定集合的對象。
對象創建端點稍微容易掌握,因為我們只是將整個有效負載傳遞給 MongoDB(方法又名免費 JSON REST API):
app.post('/collections/:collectionName', function(req, res) {
req.collection.insert(req.body, {}, function(e, results){
if (e) return next(e)
res.send(results)
})
})
單個對象檢索函數比 find()
更快 ,但它們使用不同的接口(它們直接返回對象而不是光標),所以請注意這一點。此外,我們從 :id
中提取 ID req.params.id
的部分路徑 Express.js 的魔力:
app.get('/collections/:collectionName/:id', function(req, res) {
req.collection.findOne({_id: req.collection.id(req.params.id)}, function(e, result){
if (e) return next(e)
res.send(result)
})
})
PUT 請求處理程序變得更有趣,因為 update()
不返回增強對象,而是返回我們受影響對象的計數。
還有 {$set:req.body}
是一個特殊的 MongoDB 運算符(運算符通常以美元符號開頭),用於設置值。
第二個{safe:true, multi:false}
參數是一個帶有選項的對象,告訴 MongoDB 在運行回調函數之前等待執行,並且只處理一個(第一個)項目。
app.put('/collections/:collectionName/:id', function(req, res) {
req.collection.update({_id: req.collection.id(req.params.id)}, {$set:req.body}, {safe:true, multi:false}, function(e, result){
if (e) return next(e)
res.send((result===1)?{msg:'success'}:{msg:'error'})
})
})
最後,同樣輸出自定義 JSON 消息的 DELETE 方法:
app.del('/collections/:collectionName/:id', function(req, res) {
req.collection.remove({_id: req.collection.id(req.params.id)}, function(e, result){
if (e) return next(e)
res.send((result===1)?{msg:'success'}:{msg:'error'})
})
})
注意:delete
是 JavaScript 中的運算符,因此 Express.js 使用 app.del
而是。
在這種情況下,實際上在端口 3000 上啟動服務器的最後一行:
app.listen(3000)
以防萬一有些事情不是很好,這裡是 express.js 的完整代碼 文件:
var express = require('express')
, mongoskin = require('mongoskin')
var app = express()
app.use(express.bodyParser())
var db = mongoskin.db('localhost:27017/test', {safe:true});
app.param('collectionName', function(req, res, next, collectionName){
req.collection = db.collection(collectionName)
return next()
})
app.get('/', function(req, res) {
res.send('please select a collection, e.g., /collections/messages')
})
app.get('/collections/:collectionName', function(req, res) {
req.collection.find({},{limit:10, sort: [['_id',-1]]}).toArray(function(e, results){
if (e) return next(e)
res.send(results)
})
})
app.post('/collections/:collectionName', function(req, res) {
req.collection.insert(req.body, {}, function(e, results){
if (e) return next(e)
res.send(results)
})
})
app.get('/collections/:collectionName/:id', function(req, res) {
req.collection.findOne({_id: req.collection.id(req.params.id)}, function(e, result){
if (e) return next(e)
res.send(result)
})
})
app.put('/collections/:collectionName/:id', function(req, res) {
req.collection.update({_id: req.collection.id(req.params.id)}, {$set:req.body}, {safe:true, multi:false}, function(e, result){
if (e) return next(e)
res.send((result===1)?{msg:'success'}:{msg:'error'})
})
})
app.del('/collections/:collectionName/:id', function(req, res) {
req.collection.remove({_id: req.collection.id(req.params.id)}, function(e, result){
if (e) return next(e)
res.send((result===1)?{msg:'success'}:{msg:'error'})
})
})
app.listen(3000)
退出編輯器並在終端中運行它:
$ node express.js
在另一個窗口中(不關閉第一個窗口):
$ mocha express.test.js
如果你真的不喜歡 Mocha 和/或 BDD,CURL 總是在你身邊。 :-)
例如 CURL 數據發出 POST 請求:
$ curl -d "" http://localhost:3000
GET 請求也可以在瀏覽器中使用,例如 http://localhost:3000/test。
在本教程中,我們的測試比應用程序代碼本身要長,因此放棄測試驅動開發可能很誘人,但請相信我TDD 的好習慣將為您節省大量時間 在任何嚴肅的開發過程中,當您使用的應用程序的複雜性很大時。
結論
當您需要用幾行代碼構建一個簡單的 REST API 服務器時,Express.js 和 Mongoskin 庫非常有用。稍後,如果您需要擴展這些庫,它們還提供了一種配置和組織代碼的方法。
像 MongoDB 這樣的 NoSQL 數據庫擅長免費的 REST API,我們不必定義模式,可以拋出任何數據,並且會被保存。
測試和應用文件的完整代碼:https://gist.github.com/azat-co/6075685。
如果您想了解有關 Express.js 和其他 JavaScript 庫的更多信息,請查看 Express.js 簡介系列教程。
注意 :在這個例子中,我使用了少分號的樣式。 JavaScript 中的分號是絕對可選的,除了以下兩種情況:在 for 循環中和以括號開頭的表達式/語句之前(例如,立即調用函數表達式)。