天天看點

技能學習:學習使用Node.js + Vue.js,開發前端全棧網站-8.server端使用通用CRUD接口

1.什麼是CRUD

CRUD是指在做計算處理時的增加(Create)、檢索(Retrieve)、更新(Update)和删除(Delete)幾個單詞的首字母簡寫。CRUD主要被用在描述軟體系統中資料庫或者持久層的基本操作功能。

目的是我們将常用的增、删、改、查操作封裝在一套接口元件中,在多個功能中重複調用這一套接口元件,進而使代碼更精簡,同時提升開發效率。

2.在本項目中的接口優化實作方法說明

我們根據已經寫好的單個功能接口進行改動講解。

首先,我們再次深度了解server端接口頁面:

技能學習:學習使用Node.js + Vue.js,開發前端全棧網站-8.server端使用通用CRUD接口

(1)項目中,我們以node.js作為後端進行伺服器的開發。就像PHP後端的ThinkPHP架構一樣,在node中我們使用express架構,是以接口頁面我們要首先引入express。

(2)該路由檔案指向admin端,需要使用express架構的路由進行接口的調用。

(3)引用定義好的資料庫模型,一切資料通過模型定義的字段和類型進行判斷與上傳。

(4)編寫接口。

第二步,我們要找出在不同功能子產品中調用接口時需要變化的地方,此時我們用分類功能和文章功能的兩套接口做比較。

技能學習:學習使用Node.js + Vue.js,開發前端全棧網站-8.server端使用通用CRUD接口

在圖中我們可以看出,接口方法基本相同,不同的地方就是接口路由位址和資料庫表的模型名。是以要想使用通用CRUD接口,隻需要将接口路由位址和模型名做成從前端接收資料的變量即可。

第三步,更改接口路由位址。

我們需要修改代碼參照RESTful風格,rest是一種接口設計風格,大家感興趣的百度查一查。下面我們直接代碼講解。

// 定義“admin/api”路由方法,挂載存入到router
// RESTful風格接口,在api後加一個rest字首,與程式員編寫代碼原則類似,避免以後與其他接口沖突。
// 動态resours資源,用來存放接口路由位址(原categories、articles)。這裡寫成這樣之後,上邊的接口位址名就以“/”表示。
app.use('/admin/api/rest/:resourse', router)           

将動态resourse添加到路由方法中之後,這個“:resourse”就代表了之前位址中的categories。是以在上邊的接口函數中将categories去掉,用“/”替換。

修改前:

// 上傳資料(增)
    router.post('/categories', async(req, res) => {
        // 在裡邊放該接口的具體操作,所有操作同級放置

        // 資料來源是接受到的req中的body
        // 為避免阻塞,要用異步操作,故加入await
        const model = await Category.create(req.body)
        // 接收傳回值
        res.send(model)
    })           

修改後:

// 上傳資料(增)
    router.post('/', async(req, res) => {
        // 在裡邊放該接口的具體操作,所有操作同級放置

        // 資料來源是接受到的req中的body
        // 為避免阻塞,要用異步操作,故加入await
        const model = await Category.create(req.body)
        // 接收傳回值
        res.send(model)
    })           

除位址以外都無需變化。

第四步,更改模型的引入。

接口位址修改完成後,同樣需要修改模型的引入方法。之前的全局引入方式就不能用了。

技能學習:學習使用Node.js + Vue.js,開發前端全棧網站-8.server端使用通用CRUD接口

要在接口内部引入,并且使用:

// 查詢資料(查)
    router.get('/', async(req, res) => {
        const Model = require('../../models/' + req.params.resourse)
        const items = await Model.find().populate('parent').limit(10)
        res.send(items)
    })           

第五步,接收接口位址并将接口名傳值給模型,用來當作模型名。

先把admin端axios接口位址改一下,改成rest/categories,友善之後分類清單重新整理的測試。

技能學習:學習使用Node.js + Vue.js,開發前端全棧網站-8.server端使用通用CRUD接口

接口傳值在req.params.resourse找到,并将該值放置在模型處,用來當作模型名。

此時找到分類清單頁面測試,network中找到錯誤,顯示未定義模型名:

技能學習:學習使用Node.js + Vue.js,開發前端全棧網站-8.server端使用通用CRUD接口

因為我們在檔案全局使用resourse接收,但在接口内部使用傳來的值,是以必須在router中合并接收的參數:

技能學習:學習使用Node.js + Vue.js,開發前端全棧網站-8.server端使用通用CRUD接口

修改router後,找到了模型名,但因為接口位址為分類的複數形式,是以模型名接收到的也是複數,是以此時模型名因格式不對同樣無法調用模型:

技能學習:學習使用Node.js + Vue.js,開發前端全棧網站-8.server端使用通用CRUD接口

我們此時要把接收到的模型名轉化成單數,這裡用到一個類包“inflection”,專門用來操作單複數的轉換。

cd admin           
npm i inflection           
技能學習:學習使用Node.js + Vue.js,開發前端全棧網站-8.server端使用通用CRUD接口

第六步,使用inflection類包将模型名轉化為單數形式。

// 查詢資料(查)
    router.get('/', async(req, res) => {
        // 将接收的複數位址名轉化為單數模型名
        const modelName = require('inflection').classify(req.params.resourse)
        const Model = require('../../model/' + modelName)
        const items = await Model.find().populate('parent').limit(10)
        res.send(items)
    })           

測試,沒問題:

技能學習:學習使用Node.js + Vue.js,開發前端全棧網站-8.server端使用通用CRUD接口

到這裡,一個通用CRUD接口就實作了。但是所有接口在模型名進行轉化時的操作都相同,是以我們可以在路由方法處加一個中間件的處理函數,不用每次都對model名的轉換寫一遍:

app.use('/admin/api/rest/:resourse', async (req, res, next) => {
        // 從resourse擷取路由位址名,并使用inflection的classify轉換為單數形式
        const modelName = require('inflection').classify(req.params.resourse)
        // 為請求資料挂載一個model,友善接口使用此模型名變量
        req.Model = require('../../models/' + modelName)
        next()
    }, router)           

上邊的模型名就使用中間件函數中挂載到req的model名,把接口中模型的地方全部改成req.model就可以了。

技能學習:學習使用Node.js + Vue.js,開發前端全棧網站-8.server端使用通用CRUD接口

admin端所有接口位址前加一個“rest/”

技能學習:學習使用Node.js + Vue.js,開發前端全棧網站-8.server端使用通用CRUD接口

測試所有功能,沒問題,包括分類或文章的增删改查、層級關聯。

最後上一遍接口檔案代碼:

module.exports = app => {
    const express = require('express')

    const router = express.Router({
        // 合并參數
        mergeParams: true
    })

    // 建立資料(增)
    router.post('/', async(req, res) => {
        // const Model = require('../../model/' + req.params.resourse)
        const model = await req.Model.create(req.body)
        res.send(model)
    })
    // 查詢資料(查)
    router.get('/', async(req, res) => {
        const items = await req.Model.find().populate('parent').limit(10)
        res.send(items)
    })
    // 根據id查詢資料(查)
    router.get('/:id', async(req, res) => {
        const model = await req.Model.findById(req.params.id)
        res.send(model)
    })
    // 編輯資料(改)
    router.put('/:id', async(req, res) => {
        const model = await req.Model.findByIdAndUpdate(req.params.id, req.body)
        res.send(model)
    })
    // 删除資料(删)
    router.delete('/:id', async(req, res) => {
        await req.Model.findByIdAndDelete(req.params.id, req.body)
        res.send({
            success: true
        })
    })

    // 定義“admin/api”路由方法,挂載存入到router
    // RESTful風格接口,在api後加一個rest字首,與程式員編寫代碼原則類似
    // 動态resours資源,用來存放接口路由位址(原categories、articles)。這裡寫成這樣之後,上邊的接口位址名就以“/”表示
    app.use('/admin/api/rest/:resourse', async (req, res, next) => {
        // 從resourse擷取路由位址名,并使用inflection的classify轉換為單數形式
        const modelName = require('inflection').classify(req.params.resourse)
        // 為請求資料挂載一個model,友善接口使用此模型名變量
        req.Model = require('../../models/' + modelName)
        next()
    }, router)
}           

注意,如有錯誤的地方,要檢查admin端元件中axios發送接口請求時的接口位址是否正确,并檢查NetWork中的報錯資訊。實在不行文章評論問我。