NodeJS——大汇总(一)(只需要使用这些东西,就能处理80%以上业务需求,全网最全node解决方案,吐血整理)

一、前言

本文目的

本文是博主总结了之前的自己在做的许多个项目的一些知识点,固然我在这里不会过多的解说营业的流程,而是确立一个小demon,旨在辅助人人去加倍高效 加倍便捷的天生自己的node后台接口项目,本文底部提供了一个 蓝图,迎接人人下载,start,现实上,这样的一套思绪打下来,基本上就已经确立手撸了一个nodejs框架出来了。大多数框架基本上都是这样构建出来的,底层的Node 第二层的KOA 或者express,第三层就是种种第三方包的加持。

注重:本文略长,我分了两个章节

本文写了一个功效对照齐全的博客后台治理系统,用来演示这些工具的使用,源代码已经分章节的放在了github之中,链接在文章底部

望周知

迎接列位大牛指教,若有不足望体谅,这里只是提供了一个从express过渡到其它框架的文章,现实上,这篇文章所先容的工具,也仅仅是工具啦,若是是真实开发项目,我们可能加倍青睐于选择一个成熟稳固的框架,好比AdonisJS(Node版的laravel) ,NestJS(Node版的spring),EggJS…..,我更推荐NestJS,博主后期会出一些Nest教学博文,迎接关注

至于选择Nest缘故原由如下

NodeJS——大汇总(一)(只需要使用这些东西,就能处理80%以上业务需求,全网最全node解决方案,吐血整理)

二、稀奇提醒

整体的架构思绪

  1. 隐讳

许多时刻人人做为 高技术人才(程序猿独身狗),最隐讳的事情就是什么都是还不清晰的情形下就去,吧唧的敲代码,就从小我私家的履历来谈,思绪这种器械真的异常异常的主要

  1. 从更高的条理来看架构的设计

一样平常来讲,我们可以从两个角度来看架构的设计,一个是数据,一个http报文(res,req)

  • 数据
    我们看看若是从数据的扭转角度,也就是说,我们站在数据的角度,看看整体的web架构应该若何做才是相对对照合理的.

第一步,我们拿到一个需求,要做的第一件的事情就是剖析数据确立模子
第二步,仔细的剖析数据的扭转(如下这里假设了这样的一种)

用户点点击文章的时刻,我们能举行数据的团结查询,而且把查询的数据返回给回去

NodeJS——大汇总(一)(只需要使用这些东西,就能处理80%以上业务需求,全网最全node解决方案,吐血整理)

  • 报文
    从报文的角度,看整体的架构,这里现实上也异常的简朴,就是看看我们的报文到底经过了什么加工到底获得了什么样的数据,看看req,res履历了什么,就可以很好的掌握 整个的后台的API设计架构,

NodeJS——大汇总(一)(只需要使用这些东西,就能处理80%以上业务需求,全网最全node解决方案,吐血整理)

  1. 连系

开发后台的时刻,对于一个有追求的工程师来说,二者的完善连系才是我们稳定的追求,

更快,更高效,更稳固

NodeJS——大汇总(一)(只需要使用这些东西,就能处理80%以上业务需求,全网最全node解决方案,吐血整理)

数据库建模约定

我们严酷约定:Aritcle (库) => (对应的接口)articles

我们这里有一些约定是必须要遵守的,我以为在工作中,若是遵守这些规范,可以利便后续的种种营业的操作

约定

  • 约定1

严酷要求数据库是单数而且首字母的大写形式

  • 约定2

严酷要求请求的api接口是小写的复数形式

  • 好比

Aritcle (库)  => (对应的接口)articles  

实操

好了,有了前面的约定另有理论,现在我们来实操

  1. 模子
    需求:我希望确立一个博客网站,博客网站现在有如下的数据,他们的数据模子图如下(为了利便我们使用Native的模子设计,然则现实上我们这里照样使用MongoDB数据库)

NodeJS——大汇总(一)(只需要使用这些东西,就能处理80%以上业务需求,全网最全node解决方案,吐血整理)

以上我们详细的说明晰各个数据之间的关联操作

  1. 代码实现
    工程目录如下

NodeJS——大汇总(一)(只需要使用这些东西,就能处理80%以上业务需求,全网最全node解决方案,吐血整理)

详细的代码实现,这里解说了若何在mongoose中举行多表(聚集)关联

  • 广告模子
    /model/Ad.js
const mongoose = require('mongoose')
const schema = new mongoose.Schema({
    name:{type:String},
    thumbnails:{type:String},
    url:{type:String}

})
module.exports = mongoose.model('Ad',schema)

以下的代码大多都是大同小异,我们只列出来Schema规则

  • 治理员模子
    /mode/AdminUser.js
const schema = new mongoose.Schema({
    username:{type:String},
    passowrd:{type:String}

})
  • 文章模子
    /mode/Article.js
const schema = new mongoose.Schema({
    title:{type:String},
    thumbnails:{type:String},
    body:{type:String},
    hot:{type:Number},

   // 建立时间与更新时间
    createTime: {
        type: Date,
        default: Date.now
    },
    updateTime: {
        type: Date,
        default: Date.now
    }
    
    // 一篇文章可能同属于多个分类之下
    category:[{type:mongoose.SchemaTypes.ObjectId,ref:'Category'}],

},{
      versionKey: false,//这个是示意是否自动的天生__v默认的ture示意天生
      // 这个就能做到自动治理时间了,异常的方面
    timestamps: { createdAt: 'createTime', updatedAt: 'updateTime' }
})
  • 栏目模子
    /mode/Book.js
const schema = new mongoose.Schema({
    iamge:{type:String},
    name:{type:String},
    body:{type:String},
})
  • 分类模子
    /mode/Category.js
const schema = new mongoose.Schema({
    title:{type:String},
    thumbanils:{type:String},
    
    //父分类,一篇文章,我们假设一个文章能有一个父分类,一个栏目(书籍)
    parent:{type:mongoose.SchemaTypes.ObjectId,ref:'Category'},
    book:{type:mongoose.SchemaTypes.ObjectId,ref:'Book'}

})
  • 谈论模子
    /mode/Comment.js
const schema = new mongoose.Schema({
    body:{type:String},
    isPublic:{type:Boolean}
})

他们的模子在这个文件夹下

NodeJS——大汇总(一)(只需要使用这些东西,就能处理80%以上业务需求,全网最全node解决方案,吐血整理)

REST气概约定

我们所有使用REST气概接口

REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移

明白话说就是一种API接口编写的规范,固然了这里不详细的睁开叙述,我们来看看有用的

什么是仿函数?

下面的代码就用到了一些常用的RES气概

请不要关注详细的营业逻辑,我们的总店是请求的接口的编写


    // 单一个的post不带参数就是示意----> 增 (往资源内里增加些什么)
    router.post('/api/articles', async(req, res) => {
        const model = await Article.create(req.body)
            // console.log(Article);
        res.send(model)
    })

    // 单一个get不带参数示意-------> 查 (把资源里的都查出来)
    router.get('/api/articles', async(req, res) => {

        const queryOptions = {}
        if (Article.modelName === 'Category') {
            queryOptions.populate = 'parent'
        }
        const items = await Article.find().setOptions(queryOptions).limit(10)
        res.send(items)
    })

    //get带参数示意-------> 指定条件的查
    router.get('/api/articles/:id', async(req, res) => {
        //我们的req.orane内里就又东
        console.log(req.params.id);
        const items = await Article.findById(req.params.id)
        res.send(items)
    })

    // put带参数示意-------> 更新某个指定的资源数据
    router.put('/api/articles/:id', async(req, res) => {
        const items = await Article.findByIdAndUpdate(req.params.id, req.body)
        res.send(items)
    })

    // deldete带参数示意------> 删除指定的资源数据
    router.delete('/api/articles/:id', async(req, res) => {
        await Article.findByIdAndDelete(req.params.id, req.body)
        res.send({
            sucees: true
        })
    })

message气概约定方案

我们约定,返回信息的花样res.status(200).send({ message: ‘删除乐成’ })

我们都知道,再有些情形下,我们的获得的一些效果是差不太多的,有时刻,我们希望获得一些花样上统一的数据,这样就能大大的简化前端的操作。做为一名优异的有节操的后台程序员,我们应该与前端约定一些数据的统一返回花样,这样就能大大的加速,大大的简化项目的开发

好比我习惯把一些操作的数据统一一个花样发出去
注重:我指的统一,是指没有现实的数据库讯息返回的时刻,若是有数据,就老老实实返回对应的数据就好了

  1. 假设我们删除乐成了

我们返回这样的数据


    res.status(200).send({ message: '删除乐成' })

  1. 假设我们删除失败了
    // 程序设计的一个观点:中止条件
   if (!user) {
        return res.status(400).send({ message: '删除失败' })
    }

  1. 假设我们需要权限
  if (!user) {
        return res.status(400).send({ message: '用户不存在' })
    }

以上res.status(400).send({ message: ‘用户不存在’ })就是我们的约定

中间件约定方案

中间件约定方案:我们约定一个规则去搭建我们的中间件

  • 假设有这样的一种情形,我们有一个接口要处置一项异常复杂的营业,使用了异常多的中间件,那么我该若何处置呢,

假设我们有一个接见文章详情的接口,获取的这个数据,需要有文章详情body,文章的tabs,上一篇 下一篇是否存在(也就是判断数据库中,文章之前是否另有文章)


// 文章详情页,不要关注详细的营业,我这里想表达的是。若是是多个中间件,我们就用【】括起来,而且我们严酷要求所有中间件处置之后若是有接口都必须放在req上,这样我们后续就可以异常利便的拿中间件处置的数据了,req工具,再整个node中,另有一个角色(第三方),可以用来做数据的扭转的工具

articleApp.get('/:id', 
[article.getArticleById,
 article.getTabs,
 article.getPrev, 
 article.getNext,
 category.getList,
 auth.getUser], 
 (req, res) => {
    let { article, categories, tabs, prev, next, user } = req
    res.send(
        {
            res:{ 
                // 若是key和value一样我们可以忽略掉
                 article:article,
                 categories:categories,
                 tabs,
                 prev,
                 next, 
                 user 
                }
        }
        
    )
})

主要的一个话题,错误处置中间件

我们程序执行的时刻,可能回报错,然则我们希望给用户友好的提醒,而不是直接给除报错信息,那么我们可以这样的来做,界说一个统一的错误处置中间件

注重啊,由于是整体的错误处置中间件,于是我们把整个器械放在main中的app下就好了全局的use一下,捕捉全局的错误

   // 错误处置中间件,统一的处置我们http-assart抛出的错误
    app.use(async (err,req,res,next)=>{

        // 详细的捕捉到信息是err中,再服务器为了排查错误,我们打印出来
        
        consel.log(err)

        res.status(500).send({
            message:'服务器除问题了~~~请守候修复'
        })
        
    }) 

以上就是我们的第一部门的所有内容

至此我们项目的文件夹如下

NodeJS——大汇总(一)(只需要使用这些东西,就能处理80%以上业务需求,全网最全node解决方案,吐血整理)

一款异常好用的REST测试插件

这里先容了一个异常好用的接口测试工具RESTClinet
/.http


@uri =  http://127.0.0.1:3333/api



### 接口测试
GET {{uri}}/test


### 获取JSON数据
GET {{uri}}/getjson



### 后去六位数验证码
GET {{uri}}/getcode


###### 正式的对数据库操作 #########

### 验证用户是否存在
GET {{uri}}/validataName/bmlaoli



### 增:====> 实现用户注册
POST {{uri}}/doRegister
Content-Type: application/json

{
    "name":"123123",
    "gender":"男",
    "isDelete":"true"
}


### 删:====> 凭据id举行数据库的某一项删除
DELETE  {{uri}}/deletes/9


### 改:====> 凭据id修改某个数据的详细的值
PATCH  {{uri}}/changedata/7
Content-Type: application/json

{   
    "name":"李仕增",
    "gender":"男",
    "isDelete":"true"
}


### 查: =====> 获取最真实的数据
GET  {{uri}}/getalldata



###  天生指定的表内里的项
GET {{uri}}/createTable


三、进入正题

跨域的解决发方案

cros模块的使用

我们使用一个cros,

const cors = require('cors')
app.use(cors())

静态资源的解决方案

express就好了

我们使用一个express就能解决了

// 文件上传的文件夹模块设置,同时也是静态资源的处置,
app.use('/uploads', express.static(__dirname + '/uploads')) //静态路由

post请求处置方案

对于post的解决方案异常的简朴,我们只需要使用express为我们提供的一些工具就好了

// 以下两个专门用来处置application/x-www-form-urlencoded,application/json花样的post请求

app.uer(express.urlencoded({extended:true}))
app.use(express.json())

数据库解决方案

解说要点:model操作,connet’,popuerlate查询语句

  1. 基础知识

这里我们使用的MongoDB数据库。我们只需要确立模子之后拿到数据表(聚集)的操作模子就可以了,模子我们之前是已经界说过的,异常的简朴,我们只需要确立链接,而且拿来操作就好了
/plugin/db.js

module.exports = app => {
//  使用app有一个利益就是这些项我们都是可以设置的,这个app现实上你写成option也没问题
    const mongoose = require("mongoose")
    mongoose.connect('mongodb://127.0.0.1:27017/Commet-Tools', {
        useNewUrlParser: true,
        useUnifiedTopology: true
    })
}

/index.js

require('./plugin/db')(app)
  1. 假设有一个接口要求查询数据那么可以这样,使用mongoose的ORM方式
    router.post('/api/articles', async(req, res) => {
        const model = await req.Model.create(req.body)
            // console.log(req.Model);
        res.send(model)
    })

CRUD解决方案

CRUD营业逻辑

这里我们主要使用
我们看看我们现在的项目目录结构,再看看我们的CRUD营业逻辑代码

  1. 入口
    /index.js
const express = require('express')
const app = express()

// POST解决方案
app.uer(express.urlencoded({extended:true}))
app.use(express.json())


require('./plugin/db')(app)
require('./route/admin/index')(app)


app.listen(3000,()=>{
    console.log('http://localhost:3000');
})
  1. 子路由CRUD接口逻辑所在
    /router/admin/index.js

    // 单一个的post不带参数就是示意----> 增 (往资源内里增加些什么)
    router.post('/api/articles', async(req, res) => {
        const model = await Article.create(req.body)
            // console.log(Article);
        res.send(model)
    })

    // 单一个get不带参数示意-------> 查 (把资源里的都查出来)
    router.get('/api/articles', async(req, res) => {

        const queryOptions = {}
        if (Article.modelName === 'Category') {
            queryOptions.populate = 'parent'
        }
        const items = await Article.find().setOptions(queryOptions).limit(10)
        res.send(items)
    })

    //get带参数示意-------> 指定条件的查
    router.get('/api/articles/:id', async(req, res) => {
        //我们的req.orane内里就又东
        console.log(req.params.id);
        const items = await Article.findById(req.params.id)
        res.send(items)
    })

    // put带参数示意-------> 更新某个指定的资源数据
    router.put('/api/articles/:id', async(req, res) => {
        const items = await Article.findByIdAndUpdate(req.params.id, req.body)
        res.send(items)
    })

    // deldete带参数示意------> 删除指定的资源数据
    router.delete('/api/articles/:id', async(req, res) => {
        await Article.findByIdAndDelete(req.params.id, req.body)
        res.send({
            sucees: true
        })
    })

    // 使用router 这一步一定不能少
    app.use('/api',router)

  1. 测试效果

NodeJS——大汇总(一)(只需要使用这些东西,就能处理80%以上业务需求,全网最全node解决方案,吐血整理)

REST测试文件如下

@uri =  http://localhost:3001/api

### 测试
GET {{uri}}/test


### 增
POST {{uri}}/articles
Content-Type: application/json

{
    "title":"测试题目3",
    "thumbnails":"http://www.mongoing.com/wp-content/uploads/2016/01/MongoDB-%E6%A8%A1%E5%BC%8F%E8%AE%BE%E8%AE%A1%E8%BF%9B%E9%98%B6%E6%A1%88%E4%BE%8B_%E9%A1%B5%E9%9D%A2_35.png",
    "body":"<h1>这是我们的测试内容/h1>",
    "hot":522
}

### 删
DELETE {{uri}}/articles/5eca1161017fa61840905206

### 改,仅仅是更改一部门,
PUT {{uri}}/articles/5eca1161017fa61840905206
Content-Type: application/json

{
    "category":""
    "title":"测试题目2",
    "body":"<h1>这是我们的测试内容/h1>",
    "hot":522
}


### 查
GET {{uri}}/articles

### 指定的查
GET {{uri}}/articles/5eca1161017fa61840905206

通用的抽象封装

inflection

我们发现,若是是这里只是指定的一个资源(表-聚集)的CRUD,若是说我们有许多的资源,那么我们是不太可能一个一个去复制这些CRUD代码,因此,我们想的事情是封装,封装成统一的CRUD接口

我们的思绪异常的清晰也异常的简朴,在请求地址中,把资源获取出来,然后去查对应的资源模块就好了,这里我们需要来回首一下,我们之前的接口API规则另有资源命名的规则,articles====> Article,以是,这个命名规则在这里就用得上了,我们需要使用一个模块来处置大小写首字母的转化,另有单数复数的转换inflection

  1. 我们抽离一个中间件,放在要通用的CRUD资源请求中
    /middleware/resouce.js
// 我们希望中间件可以设置,这样我们就可以高阶函数
module.exports = Option=>{
    return async(req, res, next) => {
        const inflection = require('inflection')

        //转化成单数大写的字符串形式
        let moldeName = inflection.classify(req.params.resource)
        console.log(moldeName); //categorys ===> Category
        //注重这里的关联查询populate方式,内里放的就是一个要被关联的字段
        req.Model = require(`../model/${moldeName}`)
        req.modelNmae = moldeName
        next()
    }
} 


/router/admin/index.js

app.use('/api/rest/:resource', resourceMiddelWeare(), router)
  1. 在其他的资源中把牢固写死的资源表,替换成一个动态的表
    /router/admin/index.js
    // 单一个的post不带参数就是示意----> 增 (往资源内里增加些什么)
    router.post('/', async(req, res) => {
      
            const model = await req.Model.create(req.body)
            res.send(model)
       
    })

    // 单一个get不带参数示意-------> 查 (把资源里的都查出来)
    router.get('/', async(req, res) => {

        const queryOptions = {}
        if (req.modelName === 'Category') {
            queryOptions.populate = 'parent'
        }
        const items = await req.Model.find().setOptions(queryOptions).limit(10)
        res.send(items)
    })

    //get带参数示意-------> 指定条件的查
    router.get('/:id', async(req, res) => {
        //我们的req.orane内里就又东
        console.log(req.params.id);
        const items = await req.Model.findById(req.params.id)
        res.send(items)
    })

    // put带参数示意-------> 更新某个指定的资源数据
    router.put('/:id', async(req, res) => {
        const items = await req.Model.findByIdAndUpdate(req.params.id, req.body)
        res.send(items)
    })

    // deldete带参数示意------> 删除指定的资源数据
    router.delete('/:id', async(req, res) => {
        await req.Model.findByIdAndDelete(req.params.id, req.body)
        res.send({
            sucees: true
        })
    })

以上就是我们的一个通用的CRUD接口的编写方式了

原创文章,作者:28rg新闻网,如若转载,请注明出处:https://www.28rg.com/archives/12498.html