node+express+mysql开发后台接口

技术栈:node、express、mysql、postman(接口调试软件)、sequelize(数据库映射模型)、sequelize-auto(自动生成数据库映射模型)、apidoc(注释语法生成 webApi)、jwt(加密生成token)、formidable(文件上传)、winston 和express-winston(记录日志)、crypto/md5(加密用户密码)

一、express生成一个应用生成器

1、express + ‘项目名称’

2、cd + ‘项目名称’

3、npm install

4、项目文件一览

node+express+mysql开发后台接口_第1张图片

5、控制器相关

node+express+mysql开发后台接口_第2张图片

6、模型相关

node+express+mysql开发后台接口_第3张图片

7、二级路由相关

node+express+mysql开发后台接口_第4张图片

二、mySql数据库

1、安装数据库

2、连接数据库

3、navicat 可视化工具

三、postman接口调试工具

1、安装postman软件

2、创建这个api接口开发的项目

node+express+mysql开发后台接口_第5张图片

 

3、创建该项目下的一些分类,news,user……

node+express+mysql开发后台接口_第6张图片

4、测试接口

node+express+mysql开发后台接口_第7张图片

5、测试成功的接口保存到相应的分类里。

node+express+mysql开发后台接口_第8张图片

三、sequelize

1、sequelize介绍:官网:https://sequelize.readthedocs.io/en/v3  

2.github中文:https://github.com/demopark/sequelize-docs-Zh-CN/blob/master/querying.md

Sequelize是一个基于promise的关系型数据库ORM框架,这个库完全采用JavaScript开发并且能够用在Node.JS环境中,易于使用,支持多SQL方言(dialect),。它当前支持MySQL,、MariaDB、SQLite、PostgreSQL、Sql Server 数据库。

2、安装sequelize

1npm install --save sequelize

 

四、项目中安装MySQL

1 npm install --save mysql   (如果报错安装mysql2)

2配置db.js (如果利用自动生成数据库里边的模块,则可以直接放到index.js)

var Sequelize =  require('sequelize');
// database数据库名称   name 用户  password密码
var sequelize = new Sequelize('database', 'name', 'password', {
    host: 'localhost',  //数据库域名
    dialect: 'mysql',
    pool: {
        max: 5,
        min: 0,
        idle: 10000
    }
});
module.exports=sequelize

3配置和数据库映射的模型

var Sequelize = require('sequelize')  //引入sequelize模块
var db = require('./../db')  //引入数据库
/*
 * 定义表的映射模型
 * https://sequelize.readthedocs.io/en/v3/docs/models-definition/
 * */
module.exports = db.define('tb_user',
    {
        id: {
            type: Sequelize.INTEGER(11),
            allowNull: false,
            primaryKey: true,
            autoIncrement: true
        },
        username: {
            type: Sequelize.STRING(50),
            allowNull: false
        },
        password: {
            type: Sequelize.STRING(50),
            allowNull: false
        },
        id_card: {
            type: Sequelize.STRING(50),
            allowNull: false
        }
    }

4使用上面定义的那个user模型,进行增删改查操作。若根据查询条件在数据库中查询到数据库,则返回查询到的数据,否则返回null。

var User=require('./models/user')
/* 查询单个*/
User2.find({where:{id:1}})
.then(function (res) {
   console.log(JSON.stringify(res))
})
.catch(function (ex) {
   console.log(ex)
})

/*查询所有*/
User.findAll({where:{sex:1},attributes:['id','username']}) //包含id和username
   .then(function (res) {
      console.log(JSON.stringify(res))
   })
   .catch(function (ex) {
      console.log(ex)
   })

/*分页查询所有,有条件    注意offset,linit数据类型必须为number    */
var param = {
   'offset': 1,   // 从第几个开始查询  Number(pageSize*pageNow)
   'limit': 10,   //每次查询多少个 size
     attributes:{exclude:['password']},//exclude不包含
   where: { sex:1 }
}
User.findAndCountAll(param)
   .then(function (res) {
      console.log(JSON.stringify(res))
   })
   .catch(function (ex) {
      console.log(ex)
   })

/*添加*/
var user={
   username:'wo shi 1111111',
   password:'12312321321312312321313',
   id_card:'11011011101110110'}
User.create(user)
   .then(function (res) {
      console.log(JSON.stringify(res))
   })
   .catch(function (ex) {
      console.log(ex)
   })

/*修改*/
User.update({username:'abcdefg'},{where:{id:1}})
   .then(function (res) {
      console.log(JSON.stringify(res))  //更改成功返回 [1],失败返回[0]  
   })
   .catch(function (ex) {   
      //如果数据库里没有id这个属性名,才会进入到catch
       console.log(ex)  
   })

/*删除*/
User.destroy({where:{id:102}})
   .then(function (res) {
      console.log(JSON.stringify(res))
   })
   .catch(function (ex) {
      console.log(ex)
   })

五、sequelize-auto

上面的模型如果很多的话,一个一个操作会很麻烦。利用插件可以减少工作量。

1、官网:https://github.com/sequelize/sequelize-auto  利用sequelize-auto对照数据库自动生成相应的models,使用sequelize-auto对照数据库自动生成相应的models减少了对数据库进行增删改查时的sql语句的编写。

2、安装

1 npm install  sequelize-auto

2编写自动生成模型的auto.js

var SequelizeAuto = require('sequelize-auto')
// database数据库名称   name 用户  password密码
var auto = new SequelizeAuto('database', 'name', 'password', { 
    host: 'localhost',   //数据库地址
    dialect: 'mysql',  
    directory: './moduels',  //生成的模块放到的目录
    port: '3306',  //数据库端口
    additional: {
        timestamps: false
    }
})
auto.run(function (err) {
    if (err) throw err;
    console.log(auto.tables); // table list
    console.log(auto.foreignKeys); // foreign key list
});

3在生成的moduels文件夹下,创建一个index.js将这些自动生成的数据库模型输入:

var Sequelize =  require('sequelize');
/* 创建数据库连接 */
var sequelize = new Sequelize("hhdj","root","root",{
    host:"127.0.0.1",
    dialect:"mysql",
    underscored:true
})
/* 数据库模型名称配置及路径 */
const models=[
    {
        "name": "ApplyInfo",
        "path": "./tb_apply_info.js"
    },
    {
        "name": "Branch",
        "path": "./tb_branch.js"
    },
    {
        "name": "Carousel",
        "path": "./tb_carousel.js"
    }

]
/* 数据库模型输出 */
models.forEach(item=>{
    module.exports[item.name]=require(item.path)(sequelize,Sequelize);
})

4这样自己写比较麻烦,可以写一个自动生成index.js的createdModelsExport.js

代码里边直接将数据库链接的db.js的代码也放里边。

var fs=require('fs')  //引入文件读写模块
let files=fs.readdirSync('./moduels')  //读取所有的生成模块文件
let models = []
files.forEach(item=>{
    if(item!='index.js') {  //除了这个index.js
        /**下面这段代码结果是将: tb_user.js  转换为name为 User  **/
      let names = item.split('.')[0].split('_')  
        let name = ''
        for(let i=1;i{
    module.exports[item.name]=require(item.path)(sequelize,Sequelize);
})
`
fs.writeFile('./index.js',template,function(){
    console.log('success')
})

六、apidoc的使用

1、介绍  官网:http://apidocjs.com/  ,apidoc根据可以注释的代码生成web api文档。

2、项目中使用:(更多详细配置看官网)

1全局安装: npm install apidoc –g

2创建apidoc.json

{
  "name": " api文档", // name
  "version": "0.0.0",  //版本号
  "description": "黄淮党建api文档,版本号:0.0.0", //描述
  "title": "黄淮党建api文档", // 标题
  "url" : "localhost://3000/api"  //url前缀地址 
}

3编写需要导出的注释代码:

module.exports = [
    /**
     * @api {get} /login 登录
     * @apiName GetUser
     * @apiGroup User
     *
     * @apiParam {string} phone 用户的手机号码.
     * @apiParam {string} password 用户的登录密码.
     *
     * @apiSuccess {String} code 登录成功返回信息.
     *
     * @apiSuccessExample Success-Response:
     *     HTTP/1.1 200 OK
     *     {
 *       "phone": "17638583859",
 *       "password": "1234565"
 *     }
     *
     * @apiErrorExample Error-Response:
     *     HTTP/1.1 500 server error
     *     {
 *       "error": "server error"
 *     }
     */
]

4运行生成api网页

  命令: apidoc –i  ./myapp  -o  ./apidoc

  说明:-i myapp   myapp 注释文件所在的父文件夹

        -o apidoc   apidoc导出到哪个文件夹(自动生成)

  5打开生成的apidoc里边的index.html看一下效果

node+express+mysql开发后台接口_第9张图片

七、jwt

1、介绍:Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

2、项目中使用:

1安装 : npm install jwt-simple

2使用:在controllers/until.js编写导出设置token、验证token、以及统一返回格式方法。

普通写法:

var jwt = require('jwt-simple') //引入jwt模块
var User = require('../models').User  //引入User数据库模型

module.exports={
    setToken:function(obj) {  //设置Token
        return jwt.encode(obj, 'hhdj')  //返回加密结果   jwt.encode('加密的对象','加密的秘钥')
    },
    checkToken:function(req,res,next) {  //检查token
        var t = req.headers['token']
        if(!t) {   //如果请求头中不存在token,返回"no token"信息
            res.send(this.setResult(500,'no token'))
            return
        }
        var token = jwt.decode(t,'hhdj') //token存在,进行解密
        if(!token) {
            res.send(this.setResult(500,'token error'))  //解密不成功
            return
        }
        //解密成功,查询
        User.find({where:{id:token.id}}).then(r=>{
            req.TOKEN_USER = r  
          //将在数据里边查询的信息,保存到req. TOKEN_USER里边
            next()
        }).catch(r=>{
            res.send(this.setResult(0,'token验证失败'))
        })
    },
    /* 接口统一返回格式 */
    setResult:function(code,message='success',data=null)=>{
    return{
        code:code,
        message:message,
        data:data
    }
}
}

3用户登录控制器里调用上面定义的加密方法,给用户返回token

var User = require('../models').User
var Until = require('./until')  //同级目录下
module.exports = {
    login: function(req, res, next) {
        var username = req.body.username;  //统一接收前端的电话号,用户名,或者其他(多方式登录)
        var password = req.body.password;
        var param={
            $or:{   //$or  sequelize提供的 或者查询
                phone:username,
                id_card:username
            },
            password:password
        }
        User.findOne({where:param}).then(r=>{
            res.send(Until.setResult(200,'success', Until.setToken(r)))
        }).catch(err=>{
            res.send(Until.setResult(500,'error',err))
        })
    }
}

3需要验证是否登陆的路由里,判断用户是否登录(即是请求头里是否包含token信息)比如,user分组里,对userlist的查询请求。

Routes/index.js对路由的设置:

var express = require('express');
var router = express.Router();
var ctrls = require('../../controllers')
router.post('/user/login', ctrls.User.login);
//当进入’user/userlist’这个路由是,先调用‘ctrls.Until.checkToken’判断
//是否用户登录,如果已经登录,会在请求req里加上查询到数据,并放行。
/next() 放行后执行后面的用户列表查询方法。
router.get('/user/userlist', ctrls.Until.checkToken, ctrls.User.userlist);
module.exports = router;

如果需要导出一个web api,就把注释加上:

router.post(
    '/user/login',
    ctrls.User.login,
    /**
 * @api {post} /upload 上传文件
 * @apiVersion 1.0.0
 * @apiName upload
 * @apiGroup Common
 * @apiHeader {String} acces-token 用户token
 * @apiParam {Binary} file 上传文件
 * @apiSuccess {Number} code 结果标识
 * @apiSuccess {String} message 结果说明
 * @apiSuccess {Object} data 结果数据
 * @apiSuccessExample Success-Response:
 *     HTTP/11 200 OK
 *     {
 *       "code": 1,
 *       "mess": "成功",
 *       "data": {}
 *     }
 */);

在controllers/user.js里设置user/userlist路由的处理器

var User = require('../models').User
var Until = require('./until')  //同级目录下
module.exports = {
    userlist: function(req,res,next) {
        User.findOne({where:{id:req.TOKEN_USER.id}})
            .then(r=> {
            res.send(Until.setResult(200,'success', r))
        })
            .catch(ex=>{
            res.send(Until.setResult(500,'error'))
        })
    }
}

八、crypto

1、介绍:crypto模块的目的是为了提供通用的加密和哈希算法。用纯JavaScript代码实现这些功能不是不可能,但速度会非常慢。Nodejs用C/C++实现这些算法后,通过cypto这个模块暴露为JavaScript接口,这样用起来方便,运行速度也快。

九、控制器部分原型写法

controllers/until.js代码:

var jwt = require('jwt-simple')
var User = require('../models').User
var md5=require('md5')
module.exports = function () {
    /* 设置token */
    this.setToken = (obj) => {
        return jwt.encode(obj, 'hhdjsystem')
    }
    /* 接口统一返回格式 */
    this.setResult=(code,message='success',data=null)=>{
        return{
            code:code,
            message:message,
            data:data
        }
    }
}

controllers/user.js代码:

var User = require('../models').User
var Until = require('./Until’)
function UserController() {
    this.userList = (req, res, next) => {
        var page = Number(req.query.page),
            size = Number(req.query.size)
        if (isNaN(page)) {
            page = 1
        }
        if (isNaN(size)) {
            size = 10
        }
     }
UserController.prototype = new Until ()
module.exports = new UserController()

十、二级路由模块化写法

按照上面那种写法,所有的二级路由全放到router/index里边,不便于维护,代码量也会很多。可以分开去写他们。

1router/index/user.js导出所有user分组下的路由控制和注释

var ctrl =require('../../controllers/UserController')
module.exports=[
 /**
 * @api {post} /user/userLogin 用户登录
 * @apiVersion 1.0.0
 * @apiName userLogin
 * @apiGroup User
 * @apiParam {String} account 用户的用户名或者手机号
 * @apiParam {String} password 用户的密码
 * @apiSuccess {Number} code 结果标识
 * @apiSuccess {String} message 结果说明
 * @apiSuccess {Object} data 用户token
 * @apiSuccessExample Success-Response:
 *     HTTP/11 200 OK
 *     {
 *       "code": 1,
 *       "mess": "成功",
 *       "data": {}
 *     }
 */
    {
        router:"/user/userLogin",
        method:"post",
        ctrls:[ctrl.userLogin]
    },

 /**
 * @api {get} /user/userList 用户列表
 * @apiVersion 1.0.0
 * @apiName userList
 * @apiGroup User
 * @apiParam {Number} page 用户的用户名或者手机号
 * @apiParam {Number} size 用户的密码
 * @apiSuccess {Number} code 结果标识
 * @apiSuccess {String} message 结果说明
 * @apiSuccess {Object} data 结果数据
 * @apiSuccessExample Success-Response:
 *     HTTP/11 200 OK
 *     {
 *       "code": 1,
 *       "mess": "成功",
 *       "data": {}
 *     }
 */
    {
        router:"/user/userList",
        method:"get",
        ctrls:[ctrl.userList]
    },]

十一、formidable

1、介绍:用于解析表单数据的Node.js模块,特别是文件上传。官网:https://www.npmjs.com/package/formidable

api:

1创建一个新的传入表单:var  form  = new formidable.IncomingForm()  

2设置表单字段编码:form.encoding = ‘utf-8’

3设置放置上传文件的目录(需要手动提前创建),可以通过fs.rename,修改文件默认名:form.uploadDir = “./upload”

4如果要将写入的文件form.uploadDir包含原始文件的扩展名,请将此属性设置为true  : form.keepExtensions = false

5form.type  取决于传入的请求,“multipart”或“urlencoded”

6将所有字段的内存数量限制一共可以分配的字节(文件除外),如果超过此值,“error”则会发出事件。默认大小为2MB: form.maxFieldsSize = 2*1024*1024

7 form.maxFields=1000 限制查询字符串解析器解码的字段数。默认1000(0为无限制)

8form.hash=false   如果想要为传入文件计算校验和,设置为“sha1”或“md5”

9form.multiples = false   启动此项,该files参数包含提交多个输入文件的数组。

10form.bytesReceived   接收到总字节数

11form.parse(req,function(err,filds,files))  解析请求req中包含的表单数据,fields是解析的表单的普通数据,files是所有文件数据,通过回调函数处理。

 

Event:

1 form.on(‘progress’,function(bytesReceived,bytesExpected){})   在每个传入的数据块已被解析后发出,可以用来设置进度条。

2form.on(‘field’,function(name,value){})   每当接收到字段/值时发出。

3form.on(‘fileBegin’,function(name,file){})  在上传流中检测到新文件的发出,如果要在文件系统缓存上传文件到其他位置,使用此事件。

4form.on(‘file’,function(name,file))  每当接收到一个字段/文件时发送。

5form.on(‘error’,function(err){})   当传入的表单出错时,遇到错误的请求会自动暂停。

6form.on(‘aborted’,function(){})  当请求被用户终止时

7

form.on(‘end’,function(){}) 当收到整个请求时发出,并且所有包含的文件已经完成刷新到磁盘,这里是发送回复的最好地方。

 

2、项目中使用

1安装:npm install formidable

2编写上传文件的控制器:controllers/upload.js

var Until = require('./until')  //同级目录下
var formidable=require('formidable') //引入formidable对象
module.exports={
    upload:function (req,res,next){
        var form=new formidable.IncomingForm() //创建一个新的传入表单
        form.uploadDir='../upload'//上传文件夹
        form.multiples=true//支持多文件
        form.keepExtensions=true //是否保存上传文件的扩展名
        form.parse(req,(err,fields,files)=>{
            var files=files.file//上传文件名称myfile和前端设置的name要一样  
            var paths=[]
            if(!files){
                res.send(Until.setResult(0,'没有文件'))
                return
            }
            if(files instanceof Array){
                for(var i=0;i

 

十二、winston 和 express-winston

1、介绍:Winston是Node.js最流行的日志框架之一,设计为一个简单通用的日志库,支持多传输(在Winston中,一个传输实质上代表储存设备,也就是数据最终保存在哪里),每个Winston实例都可以对不同级别的日志配置不同的传输。官网:https://www.npmjs.com/package/winston

express-winston为您的express.js应用程序的请求和错误记录提供了中间件。官网:https://www.npmjs.com/package/express-winston

2、使用:

1安装:npm install winston express-winston --save-dev

2创建存放成功和失败日志的文件夹。

3在app.js引入这两个模块

var winston =require('winston');
var expressWinston =require('express-winston');

// 正常请求的日志
app.use(expressWinston.logger({
    transports: [
        new (winston.transports.Console)({
            json: true,
            colorize: true
        }),
        new winston.transports.File({
            filename: 'logs/success.log'  //成功的日志记录在log/success.log
        })
    ]
}));

//路由
app.use('/api', api);

//记录错误的日志信息
app.use(expressWinston.errorLogger({
    transports: [
        new winston.transports.Console({
            json: true,
            colorize: true
        }),
        new winston.transports.File({
            filename: 'logs/error.log'   //失败的日志记录在log/success.log
        })
    ]
}));
module.exports = app;

十三、跨域配置

在app.js里边添加允许跨域的代码:

var app = express();
//设置跨域访问
app.all('*',function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
    res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');

    if (req.method == 'OPTIONS') {
        res.send(200);
        //让options请求快速返回
    }
    else {
        next();
    }
});

 

十四、使用supervisor工具

实时监控我们服务端代码的变化,如果使用了这个工具,不需要每次修改完服端的代码之后的重启。我们使用全局安装即可:npm install supervisor -g

官网:https://www.npmjs.com/package/supervisor

安装完毕之后,可以使用如下命令来启动项目:

十五、使用pm2

1启动命令:pm2 start ./www

2停止命令:pm2 stop ./www

2重启运行:pm2 start ./www -f

 

十六、参数

Get:req.query.xx  

Post:req.body.xx

 

你可能感兴趣的:(node.js)