我转过几个弯 绕过那个小雨楼
拿着蒲扇摆着衣衫渡着紧箍咒
不问天涯不停留 喝过几壶酒
不过年少白头道义放胸口
倘若明天之后 遥看前尘剑封侯
似那天上神仙无所求
朝朝暮暮君如梦醒十分不为何理由
是真是假是惶恐是无休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
又过了几个弯 算尽天量道莫慌
踏这田园闻这芳草香
跌跌撞撞仗剑天涯折煞不枉无笔良
是梦是幻是温柔是家乡
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
谁能与我一醉方休
koa框架24 整合1
整合一下服务:
核心模块
- koa 框架
- koa-router 路由
- koa-static 发送静态文件(静态化文件)
- promise-fs 文件读写
- uuid 全局唯一标识符
数据处理
- koa-better-body post数据和文件
- koa-convert koa-better-body的转换async方式的插件
- memorystream 内存流,高速数据生成
- promise-mysql 数据库
session
- koa-session session
- redis和bluebird redis
后端渲染
- koa-ejs
目录结构
- libs 用于存放自己写的通用模块
- log 用于存放日志的
- router 路由文件
- static 静态资源
- template 模板文件
- upload 上传文件
创建一个config.js文件,用于配置服务器基本设置,如mysql的用户名信息那些
一个server.js文件,服务器主文件
初始化项目
npm init -y
npm i koa koa-router koa-static promise-fs uuid koa-better-body koa-convert memorystream promise-mysql koa-session redis bluebird koa-ejs -D
安装完后创建基本的server服务
const Koa = require("koa");
const config = require("./config");
//server
let server = new Koa();
server.listen(config.port);
console.log(`server running at ${config.port}`);
config设置一个端口
module.exports = {
port: 8081
}
数据库及redis
数据库的引入,一般会作为一个通用的模块单独写一个js,丢到libs目录里面,这样的话大大简化server.js服务器主文件
服务器的设置也是写在config.js文件里面
config
module.exports = {
//服务器
port: 8081,
//数据库
sql: {
host: "localhost",
prot: 3306,
user: "root",
password: "123456",
database: "koa"
}
}
libs下的mysql.js
const mysql = require("promise-mysql");
const config = require("../config");
module.exports = mysql.createPool(config.sql);
引入primise-mysql,引入config,然后直接返回一个连接池
server.js
const Koa = require("koa");
const config = require("./config");
//server
let server = new Koa();
//数据库
(async () => {
server.context.db = await require("./libs/mysql");
//监听
server.listen(config.port);
console.log(`server running at ${config.port}`);
})();
由于返回的是一个异步的,通过一个自运行的async配置awite转换,为此,监听需要在数据库连接完毕后才能运行。
数据库连接完成
redis
redis和mysql一样,在libs里面创建一个js模块,然后导出。
redis由于模块的问题,需要bluebrid的promisefyAll方法进行转换旧的异步方法。
redis也需要配置连接设置,也是放在config.js里面
config.js
module.exports = {
//服务器
port: 8081,
//数据库
sql: {
host: "localhost",
prot: 3306,
user: "root",
password: "123456",
database: "koa"
},
//redis
radsi: {
host: "localhost"
}
}
redis.js
const redis = require("redis");
const bluebird = require("bluebird");
const config = require("../config");
//连接redis
let client = redis.createClient(config.redis);
//转换旧方法
bluebird.promisifyAll(redis.RedisClient.prototype);
module.exports = client;
server.js
const Koa = require("koa");
const config = require("./config");
//server
let server = new Koa();
(async () => {
//数据库
server.context.db = await require("./libs/mysql");
//Redis
server.context.redis = await require("./libs/redis");
//测试redis
server.use(async ctx => {
// ctx.redis.setAsync("name", "mulingyuer");
ctx.body = await ctx.redis.getAsync("name");
})
//监听
server.listen(config.port);
console.log(`server running at ${config.port}`);
})();
redis连接完毕
处理redis的报错
如果中途redis服务关闭了,我们会发现整个server服务都会停止运行,并且输出错误。
原因是因为redis他发现他的错误没有人监听,那么就往上抛出,也就是throw了,这样的话整个server就会因为这个throw而停止。
而redis也提供了两个事件,有了事件监听,错误就不会往上throw了。
这样server也不会因为redis的报错而停止运行。
redis.js
const redis = require("redis");
const bluebird = require("bluebird");
const config = require("../config");
//连接redis
let client = redis.createClient(config.redis);
//转换旧方法
bluebird.promisifyAll(redis.RedisClient.prototype);
//监听错误
client.on("error", err => {
console.log("error", err.code);
});
//监听重连
client.on("reconnecting", ev => {
console.log(`try to reconnect to redis server: ${ev.attempt}`);
});
module.exports = client;
监听错误,和监听重连次数,通过ev.attempt
的属性获取的重连的次数,redis重连的话,不设置默认是无限次重连,而且他这个重连也是有间隔的,不会隔几秒连一次,他是连的次数越多,隔的时间就越久。
opn和网络提示
open
opn是一个自动打开浏览器的插件,就是说我们服务器启动后,给opn传入一个地址,然后他就会自动打开浏览器并指向传入的地址页面。
npm i opn
server.js
const Koa = require("koa");
const config = require("./config");
const opn = require("opn");
//server
let server = new Koa();
(async () => {
//数据库
server.context.db = await require("./libs/mysql");
//Redis
server.context.redis = await require("./libs/redis");
//监听
server.listen(config.port);
console.log(`server running at ${config.port}`);
opn(`http://localhost:${config.port}`, {
app: 'chrome'
});
})();
opn官方示例:
const opn = require('opn');
// opens the image in the default image viewer
opn('unicorn.png').then(() => {
// image viewer closed
});
// opens the url in the default browser
opn('http://sindresorhus.com');
// specify the app to open in
opn('http://sindresorhus.com', {
app: 'firefox'
});
// specify app arguments
opn('http://sindresorhus.com', {
app: ['google chrome', '--incognito']
});
网络提示
我们需要一个原生的模块os,这个模块有一个方法networkInterfaces(),他会返回服务器的网卡信息,是一个键值对对象,网卡的名字为key,key对应的信息是一个数组,数组里面存放了很多对象,每个对象都有对应的IPv4或者v6的信息。
我们需要先获取所有网卡信息 ----- 跳过VMware虚拟网卡 ---- 遍历非VMware网卡的数组 ------ 找到对象里面有IPv4的对象,拿到这个对象的address,也就是真实ip地址。
一般有两个,一个是外网ip,一个是本地的127.0.0.1
然后将ip信息保存在一个数组里面,依次console输出就行了。
也是一样,在libs里面创建一个network.js模块
network.js
const os = require("os");
const networkObj = os.networkInterfaces();
let addressArr = [];
for (let key in networkObj) {
if (!key.startsWith("VMware")) {
networkObj[key].forEach(item => {
if (item.family === "IPv4") {
addressArr.push(item.address);
}
});
}
};
module.exports = function(port) {
if (port === 80) {
addressArr.forEach(ip => {
const address = ip === "127.0.0.1" ? "localhost" : ip;
console.log(`server running at http://${address}`);
});
} else {
addressArr.forEach(ip => {
const address = ip === "127.0.0.1" ? "localhost" : ip;
console.log(`server running at http://${address}:${port}`);
});
}
};
我直接把输出也丢里面,弄了一个方法判断端口,如果是默认80端口,就不输出端口,其他则带端口一起输出
server.js
const Koa = require("koa");
const config = require("./config");
const opn = require("opn");
const network = require("./libs/network");
//server
let server = new Koa();
(async () => {
//数据库
server.context.db = await require("./libs/mysql");
//Redis
server.context.redis = await require("./libs/redis");
//监听
server.listen(config.port);
//输出运行地址
network(config.port);
opn(`http://localhost:${config.port}`, {
app: 'chrome'
});
})();
post和上传文件
之前说过post和上传文件的处理方式:
- post的话,multipart和buffer都是false,禁止上传
- 上传文件的话,需要设置上传路径,单个文件大小
而且这两个由于是多个地方需要使用,我们需要封装一下。
config.js
配置一下默认地址和默认文件大小
const path = require("path");
module.exports = {
//服务器
port: 8081,
//数据库
sql: {
host: "localhost",
prot: 3306,
user: "root",
password: "123456",
database: "koa"
},
//redis
radsi: {
host: "localhost",
port: 6379,
pass: undefined, //redis密码为空用undefined表示
},
//upload
up_path: path.resolve(__dirname, "upload"), //默认上传路径
up_size: 20 * 1024 * 1024, //默认文件大小
}
libs/post-body.js
const body = require("koa-better-body");
const convert = require("koa-convert");
const config = require("../config");
module.exports = {
post() {
return convert(body({
multipart: false,
buffer: false
}))
},
upload(options) {
//配置参数
options = options || {};
options.path = options.path || config.up_path;
options.size = options.size || config.up_size;
return [
async (ctx, next) => {
try {
await next();
} catch (e) {
if (e.message.startsWith("maxFileSize exceeded")) {
if (options.sizeError) {
options.sizeError(ctx, e);
} else {
ctx.status = 407;
ctx.body = "文件过大";
}
} else {
if (options.error) {
options.error(ctx, e);
} else {
throw e;
}
}
}
},
convert(body({
uploadDir: options.path,
maxFileSize: options.size
}))
];
}
}
这里post就很简单了,return一个就行了,上传的文件的话,我们要考虑到参数。
用户除了默认的话,还可以自定义文件大小,路径,以及错误回调和文件超出大小的错误回调。
在错误回调里面我们可能需要给浏览器返回内容,所以需要传入ctx对象。
server.js
const Koa = require("koa");
const config = require("./config");
const opn = require("opn");
const network = require("./libs/network");
const {
post,
upload
} = require("./libs/post-body");
const Router = require("koa-router");
//server
let server = new Koa();
(async () => {
//数据库
server.context.db = await require("./libs/mysql");
//Redis
server.context.redis = await require("./libs/redis");
//全局错误处理
server.use(async (ctx, next) => {
try {
await next();
} catch (e) {
ctx.status = 500;
ctx.body = 'internal server error';
}
});
//router
let router = new Router();
//post
router.post("/post", post(), async ctx => {
console.log(ctx.request.fields);
});
//upload
router.post("/upload", ...upload({
path: undefined,
size: undefined,
error: (ctx, e) => {}, //错误回调
sizeError: (ctx, e) => {} //文件过大的错误回调
}), async ctx => {
console.log(ctx.request.fields);
ctx.body = '上传成功';
});
//激活router
server.use(router.routes());
//监听
server.listen(config.port);
//输出运行地址
network(config.port);
opn(`http://localhost:${config.port}`, {
app: 'chrome'
});
})();
引入post-body后,创建路由,通过路由来对post和上传文件进行定义接口。
注意:
由于我们的错误会抛出,所以会导致server运行停止,所以要在最上层,创建一个全局的错误处理函数,将错误捕获,以免导致运行停止。
评论(0)