1. 概述
时间跨度有点大,之前就跟大家唠嗑过一些知识点,也开启了一个Node书写一个后台api项目的开始,出于各种原因,迟迟没有更新博文。现在我把这个坑填上,如果你还有阅读过我之前的文章,我建议你先阅读一下
博文地址https://www.cnblogs.com/BM-laoli/p/12708342.html
项目Git地址 https://github.com/BM-laoli/Node-api-Design 所有代码已经分章节的放在了github供需要的朋友参考
在接下来的项目中,我将尽量的以工作时的状态,或者说完全按照公司工作的标准流程,来进行开发,git上分章节,创建分支都是必须学的基本技能,
2. 初始化(github相关)
我们这里假设一种情况,就是你进到一家公司,公司新的项目还没有开始,目前是做的旧的项目,(假设旧的项目在github上),突然你的上司跟你说,你今天做一个对某项目(这里假设旧是我们的design)做一个CRUD接口,然后你上司丢给你一个gihub地址,那你怎么办?蒙圈了???,你这个时候去问人....就有点傻逼了,因为你这都不会,你还来公司干啥?干坐着?
好了毒鸡汤灌输完毕,接下里我们就来开始我们的项目
2.1. 设计架构的更新
- 首先我们看看我们的之前的设计图,再看看我们现在的总体架构图
2.2 看看我们接下需要做哪些事情,
我们希望写几个接口对两个数据表进行操作这两个表是 article 还有user
- 关于架构的设计模式还有一些规则,
大佬不要喷我,这里就说一下,我的个人观点,项目的架构,基本上都是MVC,既然是MVC那么这里就得非常的明确的分出来,(如果你学习过后台的语言比如php或者java)那么这个应该能非常好理解,在node中的架构是这样的,
我们站在http发送的报文角度,看看我们的的报文从客户端发到我们node服务器,经历了哪些事情
--> 进入路由 -----> 路由接受到之后做业务处理,这里就是(中间件)业务处理的逻辑就是在中间件做的-------> 假设你的http报文要请求数据库的一些字段,那么久需要用到model层<------数据库的dao层(在node中你可能使用的别人大神给你写好的dao层比如mongoose,或则Knex.js等,总而言之这些东西都是为model服务的) ------>中间件抛出数据next到我们的路由上------->路由通过res.send把数据丢出去,提示:在node中我们最常用的就是把传出去的数据整成json格式,如果你还不会,别着急我们一点点来学
2.3 克隆项目下来
git clone https://github.com/BM-laoli/Node-api-Design.git
3. 进入业务之前的说明
3.1 想清楚再敲代码,别一上来就吧唧敲一顿
我们看看啊,我们的大体的流程,理解整个的流程,丢代码的书写非常的重要,这样你就能清晰的知道自己到底在干嘛了.
还有一点需要说明的是:这里我们遵守这样的一个命名规范:数据模型是大写单数,路由参数是小写复数比如
Article ====> articles
3.2 接口测试工具
我们使用一个vs上的一个插件这个插件是下面的样子,在项目中,我讲一点点的教大家使用它
4. 进入业务
4.1 设计数据库并且完成连接
这里我们主要干的一个事情就是设计两个集合(表),并且拿到他们的操作对象
- 新建一个分支init-database,
git checkout -b init-basese
这里我们新建一个分支,并且配置一个忽略项文件
.gitignore
.DS_Store
node_modules
npm-debug.log
package-lock.json
以上的代码表示我们在git上提交东西的时候可以忽略掉node_module,等文件夹/文件
- 在molde下设计一个article表(集合),它的规则(schema)如下
/model/Article
const mongoose = require(‘mongoose‘)
const schema = new mongoose.Schema({
title:{ type:String },
body:{type:String}
})
module.exports = mongoose.model( ‘Article‘,schema )
- 提交给git分支,并且合并一下代码待maseter上
git push origin init-basese
4.2 实现丢article表的增删改查(CRUD)
首先我们依然是git出来一个分支,我们在CRUD分支下进行操作
git checkout -b CRUD
注意,这里我在原来的基础上做了一些修改,因为使用restClinet的时候有些字段不能读取出来,所有我修改了下面的代码
- 在main下 我把原来的index接口改掉了,改造成了下面的这样的格式,然后在home.index.js中也稍微做了一下修改
/main.js
require(‘./API/home/index‘)(admin)
实际上这样的写法等价于。
main.use(require(‘./API/home/index‘))
/home/index.js
module.exports = admin => {
const express = require(‘express‘)
const router = express.Router({})
const Article = require(‘../../../../model/Article‘)
//测试路由
router.get(‘/test‘,(req,res)=>{
res.send(‘ok‘)
})
//这句use一定要加
admin.use(‘/CRUD‘,router)
}
- 在路由验证器的东西上,我改掉了header.token。因为我在发生restClint的时候,不走token,而是加在了Authorization字段中
/Mideleware/loginPash.js
let token = req.headers.authorization;
- 测试结果,注意这这里我使用了restClint插件,通过下面的图,你应该可以非常清晰的看到如何使用这个插件。非常方便的做接口测试
接下里就是正式的增删改查业务流程了
特别说明:这里我们编写的接口要求符合REST风格,至于说明是REST风格请去百度,它看起来就是下面的这个样子
更具REST风格来说。PUT表示更新,POST表示增加,DELET表示删除,GET分为带参数还有不带参数的情况,不带参数表示全查,待参数表示查指定内容
@uri = http://localhost:3000/main/CRUD
@toke = eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiNWU5MTIwMTViOWI0NmYzZmE4Y2MzMjUzIiwiZXhwIjoxNTg5ODU0NTI5LCJpYXQiOjE1ODk4NTI3Mjl9.U0N1bZLUBiy2fmz1NqAF-tTelE7uO8Xld4HHFeBsbq7iY5rSNMByYUyxhC1k8ug9n_8XzChL81ruiFuwxIcOg7DXk_hdrnvpUgwwtjIPjn4kfWWVEsZGm456fo3L5cHfppOTWH5NQAtKEd7xwrhqPrEPsBUwAc7pKEAFDaGuFOc
###
GET http://localhost:3000/main/test
### 进行token校验拿到我们的token,只有拿到token之后才允许进入下面的请求环节
POST http://localhost:3000/main/login
Content-Type: application/json
{
"email":"18376621755@163.com",
"password":"123456"
}
### 我们需要携带token才能发起其它的请求
GET {{uri}}/test
Authorization: {{toke}}
### 增
POST {{uri}}/Article/
Content-Type: application/json
Authorization:{{toke}}
{
"title":"分类测试3",
"body":"<h1>你好我是标题2</h1>"
}
### 删
DELETE {{uri}}/Article/5ec330fe21d3d97d5054e5bf
Authorization: {{toke}}
### 改
PUT {{uri}}/Article/5ec32f60cd080550a4e2fba8
Content-Type: application/json
Authorization:{{toke}}
{
"title":"修改测试--222",
"body":"<h1>你好我是标题2</h1>"
}
### 查
GET {{uri}}/Article
Authorization:{{toke}}
### 查 根据id查
### 特别小心由于你在后台使用了express的动态路由,所以你不必加id=xxx
GET {{uri}}/Article/5ec32f60cd080550a4e2fba8
Content-Type: application/json
Authorization:{{toke}}
/home/index.js
module.exports = admin => {
const express = require(‘express‘)
const router = express.Router({
mergeParams: true //合并所有的url,如果你合并那么那个动态的resoulce你是拿不到的
})
const Article = require(‘../../../../model/Article‘)
//测试路由
router.get(‘/test‘,(req,res)=>{
res.send(‘ok‘)
})
//增
router.post(‘/Article‘, async (req, res) => {
const model = await Article.create(req.body)
res.send(model)
})
//查
router.get(‘/Article‘, async(req, res,next) => {
const items = await Article.find().limit(10)
res.send(items)
next()
})
//根据id查
router.get(‘/Article/:id‘, async(req, res) => {
console.log(‘asdasdasd‘);
console.log(req.params.id);
const items = await Article.findById(req.params.id)
res.send(items)
})
//更新(改)
router.put(‘/Article/:id‘, async(req, res) => {
const items = await Article.findByIdAndUpdate(req.params.id, req.body)
res.send(items)
})
//根据id参数(删)
router.delete(‘/Article/:id‘, async(req, res) => {
await Article.findByIdAndDelete(req.params.id, req.body)
res.send({
sucees: true
})
})
admin.use(‘/CRUD‘,router)
}
4.3 使用RESTClinet进行接口测试
- 测试结果如下:完美通过
4.4 收尾上传分支到git
git push origin CRUD
4.4 抽离成一个通用的CRUD
现在老板又给我吗要求了,我们需要对一个名字叫做product集合(表),进行增删改查,那么问题来了?我们难道又要写一个与上代码类似的接口吗?
答案是非必要的,我们可以把上面的接口改成通用的CRUD,这样我们就能很方便的使用它了,有新的需求来的时候,只需要修改数据模型就好了
- 首先,我们在路由的地方做一层封装
这里呢,我们就使用到了,之前的命名约定规范,(命名规范非常的重要!!!),我们使用一个第三方插件,实现大小写以及单复数的转换,然后根据转换之后的结果去找对应的 数据模型就好了
module.exports = admin => {
const express = require(‘express‘)
const inflection = require(‘inflection‘)
const router = express.Router({
mergeParams: true //合并所有的url,如果你合并那么那个动态的resoulce你是拿不到的
})
++++
//起一个到哪个台的资源就好了,注意我们的中间件
admin.use(‘/CRUD/rest/:resource‘, async(req, res, next) => {
//转化成单数大写的字符串形式
let moldeName = inflection.classify(req.params.resource)
console.log(moldeName); //categorys ===> Category
//注意这里的关联查询populate方法,里面放的就是一个要被关联的字段
req.Model = require(`../../../../model/${moldeName}`)
req.modelNmae = moldeName
next()
}, router)
}
设置好一层用来的过滤中间件之后,那么我们的CRUD就变成了一个通用的接口了,后续的操作API只需要变化接口的名字,变化数据库模型就好了,非常的方便,
//增
router.post(‘/‘, async (req, res) => {
const model = await req.Model.create(req.body)
res.send(model)
})
//查
router.get(‘/‘, async(req, res,next) => {
const items = await req.Model.find().limit(10)
res.send(items)
next()
})
//根据id查
router.get(‘/:id‘, async(req, res) => {
console.log(‘asdasdasd‘);
console.log(req.params.id);
const items = await req.Model.findById(req.params.id)
res.send(items)
})
//更新(改)
router.put(‘/:id‘, async(req, res) => {
const items = await req.Model.findByIdAndUpdate(req.params.id, req.body)
res.send(items)
})
//根据id参数(删)
router.delete(‘/:id‘, async(req, res) => {
await req.Model.findByIdAndDelete(req.params.id, req.body)
res.send({
sucees: true
})
})
接下来我们新建一个product模型进行测试
/model/Prodcut
const mongoose = require(‘mongoose‘)
const schema = new mongoose.Schema({
product_name:{ type:String },
category:{type:String}
})
module.exports = mongoose.model( ‘Product‘,schema )
测试结果如下
Node教程——API接口开发(Node版的CRUD通用接口的搭建)(MangoDB+Express_Version2)