使用node開發restful風格api,一塊兒「雲養貓」

爲啥要選用node

  1. 不須要學習額外的語言特性;
  2. 擴展網絡和數據庫的知識面;
  3. 一個字,快!

安裝環境快,一鍵式無腦安裝,不用擔憂各類繁瑣配置;
啓動服務快,只須要短短几行代碼就能生成一個web服務器;
上手更快。html

思路

經過下述步驟對使用node開發web服務有一個大概的認識
  1. 安裝數據庫,這裏選用mongodb;
  2. 定義數據模式,即數據的結構和行爲;
  3. 定義數據模型,對數據庫進行操做須要經過該模型;
  4. 定義路由,在該路由路徑被訪問時,觸發對應的函數,進行數據庫操做。

其實很簡單,就三步。經過一些代碼層面的優化,甚至只須要咱們實現第二步定義好咱們所須要的數據結構以後就能生成restful風格的api。前端

restful風格的api?

寧靜的數據接口?什麼鬼?
  • 先說api,實際上是一個很廣的命題。這裏咱們理解的api是打通先後端的交互的數據接口。而在有一些語義下,它能夠是你封裝的一個工具函數方法。
  • restful風格咱們能夠這麼理解
  1. 設計路徑:在第二、3步定義好數據結構後,經過路徑去訪問該數據資源。
  2. 定義http動詞:訪問資源後,咱們須要什麼樣的操做

咱們耳熟能詳的增刪改查其實就對應着 post、delete、put、get這四個http動詞。
例如:get /api/user/ => 查看/api/user這個路徑下全部用戶信息的資源node

嘗試開發

其實開發web服務,大部分操做就是圍繞着操做數據庫,而操做數據庫確定有額外的學習成本。畢竟這是不少前端er沒接觸過的。

就我我的而言,學習一個新的知識點,查閱官方文檔無疑是最好的辦法。react

假設已經安裝完了mongodb,咱們要操做數據庫還須要另外一個東西:mongoose,翻譯過來是貓鼬,又是一個不知道什麼鳥東西,在這兒能夠簡單的理解,經過它能夠更加方便的操做數據庫。git

其實官方的例子特別的好,跟着貓鼬一塊兒雲養貓
還記得前面將的思路和步驟嘛,首先咱們要使用node的一個框架express生成項目模板github

  1. 設計路徑:在routes文件夾建立一個cat.js文件,這個就是咱們要訪問的資源;
  2. 定義數據模型:定義數據結構、生成數據模型;
  3. 定義路由:使用express框架的路由功能,根據http動詞觸發對應函數操做數據庫;
const express = require('express');
const router = express.Router();
const mongoose = require("mongoose")

//貓的模式:假設每一隻貓都有名字和顏色的屬性。
const catSchema = mongoose.Schema({
  name: String,
  color:String
})
//貓的模型:經過傳入貓的模式,咱們有了一個模型,而後就能夠根據該模型生成好多貓了!
const CatModel = mongoose.model('Cat', catSchema)

router.get('/cat', function (req, res, next) {
  //後來貓愈來愈多,就能夠經過模型來查找小貓
  CatModel.find((err, allCats) => res.json(allCats))
});

router.post('/cat', ((req, res, next) => {
  //假設咱們要增長一隻小貓,貓咪的信息經過post請求的請求體傳輸到服務端。
  const catInfo = req.body //假如請求體內容爲{name:'小白',color:'黑色'}
  //根據貓的模型生成的小貓就有了  名字爲小白,顏色爲黑色的屬性。
  const lititleCat = new CatModel(catInfo)
  //小貓在調用save方法後保存到數據庫
  lititleCat.save((err, saved) => res.json(saved))
}))

router.delete('/cat', ((req, res, next) => {
  const { id } = req.body
  //根據傳入的貓的id,刪除該貓的信息
  CatModel.findByIdAndRemove(id, (err, removed) => res.json(removed))
}))

router.put('/cat', ((req, res, next) => {
  const { id } = req.body
  //根據傳入的貓的id,更新貓的信息
  CatModel.findByIdAndUpdate(id, { ...req.body }, { new: true }, (err, updated) => res.json(updated))
}))

這個時候啓動服務,其實未生效,這是由於咱們寫了一大堆,並無觸發,須要在入口文件app.js引入咱們的路由文件。web

//引入貓的路由
const catRouter = require('./routes/cat');
//app就是咱們的服務器,當服務器資源路徑 /api被調用時,就會映射到咱們貓的路由
app.use('/api', catRouter);
// localhoset:3000/api/cat  便是咱們要請求貓的資源的 路徑,最後經過http動詞,觸發對應的數據庫操做,咱們就生成了第一個符合restful風格的api。

進階

  1. 假設咱們要將貓的信息和它的飼養員的信息綁定在一塊兒,就須要聯表
  2. mongoose的語法充斥着各類回調,就須要async await來優化

接下來修改一下上面生成貓的例子mongodb

const express = require('express');
const router = express.Router();
const mongoose = require("mongoose")
const mongoose = require('mongoose')

+ const feederSchema = mongoose.Schema({
+  feederName: String,
+  cat: {
+    type: mongoose.Schema.Types.ObjectId,//聯表查詢必須這樣的格式來存儲對應表的_id
+    ref: 'Cat'//聯表關係的表名,注意是生成模型的類,與模型名區分開
+  }
+ })

//貓的模式:假設每一隻貓都有名字和顏色的屬性。
const catSchema = mongoose.Schema({
  name: String,
  color:String
})

+ const feederModel = mongoose.model("Feeder", feederSchema)
//貓的模型:經過傳入貓的模式,咱們有了一個模型,而後就能夠根據該模型生成好多貓了!
const CatModel = mongoose.model('Cat', catSchema)

router.get('/cat',  (req, res, next)=> {
  //後來貓愈來愈多,就能夠經過模型來查找小貓
  CatModel.find((err, allCats) => res.json(allCats))
});

router.post('/cat', async((req, res, next) => {
   //假設咱們要增長一隻小貓,貓咪的信息經過post請求的請求體傳輸到服務端。
+  const { name, color, feederName } = req.body //假如請求體內容爲{name:'小白',color:'黑色',feederName:"張三"}
+  const catInfo = { name, color } //貓的信息
+  const feederInfo = { feederName }  //飼養員信息
   //根據貓的模型生成的小貓就有了  名字爲小白,顏色爲黑色的屬性。
   const lititleCat = new CatModel(catInfo)
   //小貓在調用save方法後保存到數據庫
+  const { _id } = await lititleCat.save()
   //將小貓的id與用戶的貓關聯起來
+  const newFeeder = new feederModel({ ...feederInfo, cat: _id })
   //保存用戶信息
+  await newFeeder.save()
   //經過populate()能夠查詢聯表的屬性,exec()能夠更好的追蹤堆棧,讓查詢的函數返回的是一個完整的promise對象
+  const data = await feederModel.find().populate("cat").exec()
+  res.json(data)
  
}))

router.delete('/cat', (req, res, next) => {
  const { id } = req.body
  //根據傳入的貓的id,刪除該貓的信息
  CatModel.findByIdAndRemove(id, (err, removed) => res.json(removed))
})

router.put('/cat', (req, res, next) => {
  const { id } = req.body
  //根據傳入的貓的id,更新貓的信息
  CatModel.findByIdAndUpdate(id, { ...req.body }, { new: true }, (err, updated) => res.json(updated))
})

能夠下載postman測試一下代碼
image數據庫

優化

當你使用postman測試了幾回代碼以後你會發現每次post請求都新增重複數據,哪怕內容是沒有變化的,mongoose會爲保存時的數據添加惟一的_id標識。

按照正常的邏輯應該是express

  1. 當沒有這條數據時,插入一個新數據;
  2. 當數據存在時,根據新的內容更新它;
  3. 而判斷該數據是否存在,咱們須要一個做爲惟一標識的查詢條件

因此咱們可使用findOneAndUpdate這個api插入或更新咱們的數據,修改post請求的代碼

router.post('/cat', async((req, res, next) => {
  //假設咱們要增長一隻小貓,貓咪的信息經過post請求的請求體傳輸到服務端。
  const { name, color, feederName } = req.body //假如請求體內容爲{name:'小白',color:'黑色',feederName:"張三"}
  const catInfo = { name, color } //貓的信息
  const feederInfo = { feederName }  //飼養員信息
  //這裏根據貓的名稱做爲插入或更新的查詢標識
+ const {_id} = await CatModel.findOneAndUpdate({name},catInfo,{ upsert: true, new: true, setDefaultsOnInsert: true }).exec()
  //這裏根據飼養員的名稱做爲插入或更新的查詢標識
+ await feederModel.findOneAndUpdate({feederName},{...feederInfo,cat:_id},{ upsert: true, new: true, setDefaultsOnInsert: true })
  //經過populate()能夠查詢聯表的屬性,exec()能夠更好的追蹤堆棧,讓查詢的函數返回的是一個完整的promise對象
  const data = await feederModel.find().populate("cat").exec()
  res.json(data)
 
}))

總結

固然,這些都是node最基礎的操做,可是這些特別簡單的東西,擴展了不少知識,經過斷點調試你能夠更加深入地瞭解到http請求通訊過程當中的數據傳輸、數據庫模型的操做等等,甚至你還會開始思考如何使用node作更多的東西。
例如:
快速的接口mock,在擬定好協議以後使用node開發數據接口,提升接口聯調的效率;
甚至搭一個可視化的mock數據的平臺,經過可編輯表格操做去生成數據接口;
又或者你能夠新建一個本身的網站,使用node搭建本身的服務器等等等。

最後貼上本身的github地址:
https://github.com/kangjs7854...臭不要臉的求個star,萬分感謝~

相關文章
相關標籤/搜索