整合一下服务:

核心模块

  1. koa 框架
  2. koa-router 路由
  3. koa-static 发送静态文件(静态化文件)
  4. promise-fs 文件读写
  5. uuid 全局唯一标识符

数据处理

  1. koa-better-body post数据和文件
  2. koa-convert koa-better-body的转换async方式的插件
  3. memorystream 内存流,高速数据生成
  4. promise-mysql 数据库

session

  1. koa-session session
  2. redis和bluebird redis

后端渲染

  1. koa-ejs

目录结构

  1. libs 用于存放自己写的通用模块
  2. log 用于存放日志的
  3. router 路由文件
  4. static 静态资源
  5. template 模板文件
  6. 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和上传文件的处理方式:

  1. post的话,multipart和buffer都是false,禁止上传
  2. 上传文件的话,需要设置上传路径,单个文件大小

而且这两个由于是多个地方需要使用,我们需要封装一下。

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运行停止,所以要在最上层,创建一个全局的错误处理函数,将错误捕获,以免导致运行停止。

分类: Node 标签: nodekoa

评论

暂无评论数据

暂无评论数据

目录