文章目录
- 从0开始构建一个node服务后台
- 1.为什么使用node做服务后台?
- 2.构建一个本地服务(Express框架)
- 3.设置请求头和跨域的一些配置
- 4.app.use中间件
- 5.Mysql数据库接入(mock数据可以忽略这块)
- 6.全流程步骤
- 1.前端发起请求
- 2.node后端开始响应
- 3. /router/user.js开始匹配路径“/delete”
- 4.调用updateMyspl处理生成sql
- 5.调用封装的数据库来处理操作
- 6.将信息返回到/router/user.js的指定回调里判断后发送给前端
- 7.流程示意图![前端-后端-前端node流程](https://img-blog.csdnimg.cn/ce9a2bd568fc4b30b560cb3dc2e7b116.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a626ZuA5a6J55-l6bi_6bmE5b-X,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
从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=>{
// 前端处理逻辑
})