从0开始构建一个node服务后台

从0开始构建一个node服务后台

1.为什么使用node做服务后台?

  • Javascript运行环境,前端可以快速上手,只有查阅api的学习成本
  • 非阻塞性I/O,单进程,单线程
  • 轻量、可伸缩,适于实时数据交互应用。
  • 异步,事件驱动模型,解决高并发。(已餐厅为例,客人点完餐拿号码等待,这是其他客户可以继续点餐不会阻塞其他顾客,等饭菜做好了,厨师喊号,你找到自己对应的号,然后吃饭。厨师喊你这个过程可以称之为callback回调,这样既没有阻塞新用户的链接,也不需要维护厨师与已点餐人的关系)

2.构建一个本地服务(Express框架)

1.我们先本地创建一个8080的服务,通过下面咱们就可以本地启动一个服务了,并且可以再浏览器看到咱们启动的服务
var express = require('express'); // 引用express框架
var app = express(); // 创建这个实例
app.use(express.static(__dirname + '/public')); // 默认打开的文件,eg:http://localhost:8080,他会访问该项目路径下public的index.html文件
app.listen(8080,()=>{
  console.log('端口8080启动成功')
	// 端口监听成功回调
});
2.基于底层http去封装的express
// 底层http实现方法
var http = require("http");
var app = http.createServer(function(request, response) {
  if(request.url === '/'){
  	response.writeHead(200, {"Content-Type": "text/plain"});
    response.end("Hello world!");
  }
});

app.listen(8080, "localhost");

// express的实现方法
var express = require('express');
var app = express();
app.get('/', function (req, res) {
  res.send('Hello world!');
});

app.listen(8080);
3.我们简易尝试封装下express 和 app.get
// 实现简易封装 express 和 app.get
var http = require('http');
function express() {
    var _app =http.createServer(function (request, response) {
        console.log(request.url,'url');
         app.router[request.url](request, response)
     })
   return _app
}
app.get = (path, callback) => {
    app.router=app.router || {}
    app.router[path] = callback
}
// 实现效果
var app = express();
app.get('/',(req,res)=>{
    res.writeHead(200, {"Content-Type": "text/plain"});
    res.end("Hello world!");
})
app.listen(3000, "localhost");

3.设置请求头和跨域的一些配置

1.配置多个请求头
// 利用use.all 对所有请求设置跨域信息和一些操作
// 参数中的“*”表示对所有路径有效。get方法则是只有GET动词的HTTP请求通过该中间件,它的第一个参数是请求的路径。由于get方法的回调函数没有调用next方法
app.all('*', (req, res, next) => {
	const {
		ALLOW_ORIGIN,
		CREDENTIALS,
		HEADERS,
		ALLOW_METHODS
	} = CONFIG.CROS;
	let ol = ALLOW_ORIGIN.split(',');
  	// 可支持配置多个跨域域名
    if (ol.indexOf(req.headers.origin) >= 0) {
        res.header("Access-Control-Allow-Origin", req.headers.origin);
    	res.header("Access-Control-Allow-Credentials", CREDENTIALS);
    	res.header("Access-Control-Allow-Headers", HEADERS);
    	res.header("Access-Control-Allow-Methods", ALLOW_METHODS);
    }
	req.method === 'OPTIONS' ? res.send('CURRENT SERVICES SUPPORT CROSS DOMAIN REQUESTS!') : next();
});
2.转换下json格式数据
//数据JSON类型,因为post请求传递过来的参数存在问题,所以需要安装这个包,对post引用数据类型进行处理格式化
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
	extended: false
}));

4.app.use中间件

1.中间件next
  • 中间件(middleware)就是处理HTTP请求的函数。它最大的特点就是,一个中间件处理完,再传递给下一个中间件。
  • 接收三个参数,依次为request对象(代表HTTP请求)、response对象(代表HTTP回应),next回调函数(代表下一个中间件)。每个中间件都可以对HTTP请求(request对象)进行加工,并且决定是否调用next方法,将request对象再传给下一个中间件。
  • next不执行则不会继续往下执行
app.use("/home", function(request, response, next) {
	next()
});
app.use("/about", function(request, response, next) {
 // next()
});
app.use("/ccc", function(request, response, next) {
  next()
});
2.路由匹配的原始写法
app.use(function(request, response, next) {
  if (request.url == "/about") {
    response.writeHead(200, { "Content-Type": "text/plain" });
  } else {
    next();
  }
});
// 路由匹配不到时,兜底404页面
app.use(function(request, response, next) {
 response.writeHead(404, { "Content-Type": "text/plain" });
 response.end("404 error!\n");
});
3.结合router对路由进行升级处理
// 这种写法可以将文件拆分的更合理
app.use('/user', require('./routes/user'));
app.use((req, res) => {
	res.status(404);
	res.send('NOT FOUND!');
});
// user 内部写法
const express = require('express'),
route = express.Router()// 引用route
route.post('/add', (req, res) => {
  res.send({code:0,data:{},msg:"请求成功"})
}; // 每个单独接口的处理,通过res.send(),将信息返给前端
route.post('/add1', (req, res) => {};
route.post('/add2', (req, res) => {};
route.post('/add3', (req, res) => {};
module.exports = route

5.Mysql数据库接入(mock数据可以忽略这块)

npm i mysql 下载mysql的node包

1.远程数据库或本地数据库
// 数据库连接成功回调
connection.connect(function (err) {
  if (err) {
    console.log("连接错误");
    return;
  }
})
// 利用 promise对回调进行封装,阻塞并将异常抛出
const {code, msg, result} = await new Promise((resolve,reject)=>{
  // 数据库连接成功回调
  connection.connect(function (err) {
    if (err) {
      console.log("连接错误");
      resolve({code:1,msg:"数据库连接错误"+err,result:[]})
      return;
    }
    resolve({code :0, msg:"OK", result:[]})
  })
})
2.Mysql语句封装
  • 由于sql语句不熟,不能每次都写一遍,于是需要封装sql语句
  • node的mysql使用统一的connection.query方法进行处理

更新

/**
* name:"USER",目标库表名,
* params:{a:1},更新的字段对象集合
* primaryKey:{key:主键ke,value:主键value, isString:主键value是否为字符串}
*/ 
// 更新数据库字段
const updateMyspl = (param)=>{
    const {name,params,primaryKey:{key,value, isString =true}} = param||{}
    let before = "UPDATE `"+ baseMyspl+"`.`"+name+"` SET ";
    let middle = Object.keys(params).reduce((val,next,index)=>{
        return val + "`"+next+"` = '"+params[next]+(index!==Object.keys(params).length-1?"', ":"' ")
    },"")
    let after = "WHERE `"+ name +"`.`"+ key +"` ="+" "+ (isString?"'"+value+"'":value);
    console.log(before+middle+after,'修改指令')
    return before+middle+after
}

查询

/**
* name:"USER",目标库表名,
* params:{a:1},查询搜索的字段对象集合
* page:" 1,10 ", 分页配置,不传则全量
* like:"LIKE",匹配方式,模糊匹配或精准匹配
* sort:{ConfigID:ASC,MarketID:DESC}排序字段及其方式
*/ 
// 查询数据库字段
const queryMyspl = (param)=>{
    const {name,params,page,like="LIKE",sort} = param || {}
    let isZeroParams=Object.keys(params).length === 0 ? '' : "` WHERE "
    let before = "SELECT SQL_CALC_FOUND_ROWS * FROM `"+ name+ isZeroParams;
    let middle = Object.keys(params).reduce((val,next,index)=>{
        if(index === 0){
            return val+="`"+next+"` "+like+" '"+params[next]+"' "
        }
        return val + "AND `"+next+"` "+like+" '"+params[next]+"' "
    },"")
    let sortAry =Object.keys(sort || {})
    let after =sortAry.length ===0 ? "":sortAry.reduce((str,next,index)=>{
        if(index ===sortAry.length -1){
            str+= "`"+next+"`"+" "+ sort[next]
        }else{
            str+= "`"+next+"`"+" "+ sort[next]+", "
        }
        return str
    } ,"ORDER BY ")
    let LIMIT =page ? ` LIMIT ${page}` : ''
    console.log(before+middle+after+LIMIT,'查询指令')
    return before+middle+after+LIMIT
}

新增

/**
* name:"USER",目标库表名,
* params:{a:1},新增字段对象集合
* page:" 1,10 ", 分页配置,不传则全量
*/ 
// 数据库新增指令
const addMyspl = (param)=>{
    const {name,params} = param || {};
    let before = "INSERT INTO `"+ baseMyspl+ "`.`"+ name+"`";
    let middle = " ("+ Object.keys(params).map(item=>("`"+item+'`')).join() +") "
    let after = " ("+ Object.values(params).reduce((str,next,index)=>{
        if(index ===Object.values(params).length-1){return str+=("'"+next+"'")};
        return str+= ("'"+next+"'")+","
    },"") +") "
    console.log(before+middle+" VALUES "+ after,'新增指令')
    return before+middle+" VALUES "+ after
}
3.sql操作数据库完整代码
  • 链接数据库>>>将js语句处理成sql语句>>>数据库返回数据>>>根据状态处理好返回给前端
  • 连接数据库的异步封装
  • connection.query异步代码封装
// 和本地数库建立连接
var connection = mysql.createConnection({
  connectionLimit: 50,
  host: "localhost", //远程MySQL数据库的ip地址
  user: "jianymn_admin",
  password: "6AhEFGzDGBFBLRGG",
  database: "jianymn_admin"
})
// 利用 promise对回调进行封装,阻塞并将异常抛出
const {code, msg, result} = await new Promise((resolve,reject)=>{
  // 数据库连接成功回调
  connection.connect(function (err) {
    if (err) {
      console.log("连接错误");
      resolve({code:1,msg:"数据库连接错误"+err,result:[]})
      return;
    }
    resolve({code :0, msg:"OK", result:[]})
  })
})
// connection.query异步代码封装
return new Promise((resolve,reject)=>{
  connection.query(querySql,(err,result)=>{
    if(err){
      // 数据库操作失败统一抛错
      res.send(success(false, {msg: `SQL error: ${err}!`}));
      reject()
      return    
    }
    // 当需要查询分页时,也要查出全部的语句,根据接口自行配置
    if(isSearchList){
      connection.query("SELECT FOUND_ROWS()",(err,resultTotal)=>{
        if(err){
          res.send(success(false, {msg: `SQL error: ${err}!`}));
          reject()
          return    
        }
        const total =(resultTotal[0] || {})['FOUND_ROWS()']
        resolve({result,total})
      })  
      return
    }
    resolve({result})
  })
})

6.全流程步骤

1.前端发起请求
axios.get('/user/delete',{params:{uuid}}).then(res=>{})
2.node后端开始响应
app.use('/user', require('./routes/user')); // 请求被/user匹配到,继续向指定路径下匹配下级
app.use('/order', require('./routes/order'));
3. /router/user.js开始匹配路径“/delete”
//=>删除用户信息
route.get('/delete', (req, res) => {
  const {uuid} = req.query
  const params = {
    name:'USER',
    params:{isDelete:"1"},
    primaryKey:{key:"uuid",value:uuid}
  }
  const delUserSql=updateMyspl(params) 
  mysqlConnection({querySql:delUserSql,res})
    .then(({result})=>{
    res.send(success(true,{msg: 'Ok'}));
  })
4.调用updateMyspl处理生成sql
/**
* name:"USER",目标库表名,
* params:{a:1},更新的字段对象集合
* primaryKey:{key:主键ke,value:主键value, isString:主键value是否为字符串}
*/ 
// 更新数据库字段
const updateMyspl = (param)=>{
    const {name,params,primaryKey:{key,value, isString =true}} = param||{}
    let before = "UPDATE `"+ baseMyspl+"`.`"+name+"` SET ";
    let middle = Object.keys(params).reduce((val,next,index)=>{
        return val + "`"+next+"` = '"+params[next]+(index!==Object.keys(params).length-1?"', ":"' ")
    },"")
    let after = "WHERE `"+ name +"`.`"+ key +"` ="+" "+ (isString?"'"+value+"'":value);
    console.log(before+middle+after,'修改指令')
    return before+middle+after
}
5.调用封装的数据库来处理操作
... //链接过程省略
connection.query(querySql,(err,result)=>{
  if(err){
    res.send(success(false, {msg: `SQL error: ${err}!`}));
    reject()
    return    
  }
  if(isSearchList){
    connection.query("SELECT FOUND_ROWS()",(err,resultTotal)=>{
      if(err){
        res.send(success(false, {msg: `SQL error: ${err}!`}));
        reject()
        return    
      }
      const total =(resultTotal[0] || {})['FOUND_ROWS()']
      resolve({result,total})
    })  
    return
  }
  resolve({result})
})
})
6.将信息返回到/router/user.js的指定回调里判断后发送给前端
...
mysqlConnection({querySql:delUserSql,res})
  .then(({result})=>{
  res.send(success(true,{msg: 'Ok'}));
})
// 前端接收到信息并完成逻辑处理
axios.get('/user/delete',{params:{uuid}}).then(res=>{
		// 前端处理逻辑
})
7.流程示意图前端-后端-前端node流程
  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

家雀安知鸿鹄志

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值